十二天-双指针技术:链表问题的高效解法
一、双指针技术分类
1. 同速双指针(同向移动)
- 特点:两个指针以相同速度移动
- 适用场景: 
  - 链表逆序
- 查找倒数第 k 个元素
- 删除倒数第 n 个节点
 
2. 快慢双指针(异速移动)
- 特点:一个指针每次移动 1 步,另一个移动 2 步
- 适用场景: 
  - 检测链表环存在性
- 计算环入口和长度
- 寻找中间节点
 
二、典型应用与算法实现
2.1 链表逆序
方法一:迭代法(同速双指针)
python
def reverseList(head):
    prev = None
    curr = head
    while curr:
        next_node = curr.next  # 保存后续节点
        curr.next = prev       # 反转指针方向
        prev = curr            # 双指针同步移动
        curr = next_node
    return prev  # prev最终指向新头节点
- 复杂度:O (n) 时间,O (1) 空间
方法二:递归法
python
def reverseList(head):
    if not head or not head.next:
        return head
    new_head = reverseList(head.next)
    head.next.next = head
    head.next = None
    return new_head
- 复杂度:O (n) 时间,O (n) 空间(递归栈)
2.2 查找倒数第 k 个元素
python
def findKthFromEnd(head, k):
    slow = fast = head
    # 快指针先走k步
    for _ in range(k):
        fast = fast.next
    # 双指针同步移动
    while fast:
        slow = slow.next
        fast = fast.next
    return slow.val
- 关键点:快慢指针间距保持 k,当快指针到达末尾时,慢指针指向目标节点
2.3 删除倒数第 n 个节点
python
def removeNthFromEnd(head, n):
    dummy = ListNode(0, head)
    first = dummy
    second = dummy
    
    # 快指针先走n+1步
    for _ in range(n+1):
        first = first.next
    
    # 同步移动直到快指针到达末尾
    while first:
        first = first.next
        second = second.next
    
    # 删除操作
    second.next = second.next.next
    return dummy.next
- 技巧:使用虚拟头节点避免处理头节点删除的边界情况
2.4 检测链表环存在性
python
def hasCycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False
- 原理:若存在环,快慢指针必然相遇
2.5 计算环入口和长度
python
def detectCycle(head):
    # 第一步:检测是否存在环
    slow = fast = head
    has_cycle = False
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            has_cycle = True
            break
    if not has_cycle:
        return None
    
    # 第二步:找到环入口
    ptr1 = head
    ptr2 = slow
    while ptr1 != ptr2:
        ptr1 = ptr1.next
        ptr2 = ptr2.next
    return ptr1
def calculateCycleLength(head):
    # 先找到环内节点
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            break
    
    # 计算环长度
    count = 0
    while True:
        slow = slow.next
        count += 1
        if slow == fast:
            break
    return count
三、算法复杂度对比
| 问题类型 | 双指针方法 | 时间复杂度 | 空间复杂度 | 优势 | 
|---|---|---|---|---|
| 链表逆序 | 迭代 | O(n) | O(1) | 无栈溢出风险 | 
| 查找倒数第 k 元素 | 快慢指针 | O(n) | O(1) | 仅需一次遍历 | 
| 删除倒数第 n 节点 | 快慢指针 | O(n) | O(1) | 无需预先计算长度 | 
| 检测环存在性 | 快慢指针 | O(n) | O(1) | 最优解法 | 
| 环入口定位 | 双指针定位 | O(n) | O(1) | Floyd 判圈算法变种 | 
四、优化建议与应用场景
1. 优化技巧
- 虚拟头节点:处理头节点删除时,避免复杂的边界判断
- 指针间距控制:通过调整快慢指针的初始间距,解决不同问题
- 两次遍历:在检测环问题中,先用快慢指针检测环,再用同速指针定位入口
2. 典型应用场景
- 链表操作:LeetCode 206(反转链表)、19(删除倒数第 N 个节点)
- 环检测:LeetCode 141(环形链表)、142(环形链表 II)
- 数组问题:双指针法解决两数之和、三数之和等问题
