核心要点

  • 对撞指针:用于有序数组,左右指针向中间收缩,如两数之和、判回文、盛水容器

  • 快慢指针:一个走一步一个走两步,用于链表找中点、判环、原地去重

  • 复杂度:两个指针各遍历一次,时间 O(n)、空间 O(1),关键是想清指针移动条件

标准回答

双指针是用两个索引在序列上协同移动来降低复杂度的技巧,主要分两类:对撞指针从两端向中间逼近,适合有序数组的两数之和、回文判断、接雨水等,通过比较当前和与目标决定移动哪一端;快慢指针让两个指针以不同速度前进,适合链表找中点、判断是否成环、数组原地去重。它能把许多暴力 O(n²) 解法优化到 O(n)。下面给出有序数组两数之和(对撞)和快慢指针找链表中点两个例子:

python
def two_sum_sorted(nums, target):
    # 对撞指针:nums 已升序排列
    left, right = 0, len(nums) - 1
    while left < right:
        s = nums[left] + nums[right]
        if s == target:
            return [left, right]
        elif s < target:
            left += 1     # 和偏小,左指针右移增大
        else:
            right -= 1    # 和偏大,右指针左移减小
    return [-1, -1]


class ListNode:
    def __init__(self, val=0, nxt=None):
        self.val = val
        self.next = nxt


def middle_node(head):
    # 快慢指针:fast 走两步、slow 走一步,fast 到尾时 slow 在中点
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow


if __name__ == '__main__':
    print(two_sum_sorted([2, 7, 11, 15], 9))   # [0, 1]
    # 链表 1->2->3->4->5,中点为 3
    head = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5)))))
    print(middle_node(head).val)                # 3

常见误区

⚠️ 常见踩坑

对撞指针要求数组有序,无序时需先排序或改用哈希;快慢指针的循环条件要写 fast and fast.next,否则偶数长度链表会空指针。

追问

追问 1对撞指针为什么能保证不漏解?

因为数组有序:当 nums[left]+nums[right] < target 时,固定 right 配任何更小的左元素都更小,故 left 必须右移;反之 right 左移。每一步都安全排除一个不可能的端点,所以遍历一遍即可,时间 O(n)。

追问 2链表长度为偶数时 middle_node 返回前中点还是后中点?

本写法返回靠后的中点(如 1->2->3->4 返回 3)。若想返回前中点,把循环条件改成 while fast.next and fast.next.next 即可。

延伸学习

与本题相关的知识库文章、术语、工具与行业资讯。