一、指针:内存的“快递单号”
指针可以理解为数据的地址标签,就像快递单号对应包裹的位置一样。在程序中,指针是一个变量,存储的是其他数据在内存中的具体位置(地址),而不是数据本身的值。
原理:
假设内存是一个超大的仓库,每个小格子(字节)都有唯一的编号(地址)。指针就是记录某个格子的编号,通过这个编号可以直接找到对应位置的数据。例如,`int *p = &a;`表示指针`p`记录了变量`a`在内存中的“门牌号”。
用途:
通过指针可以直接操作内存中的数据,比如修改值、传递大型数据(避免复制整个数据)或动态分配内存(灵活管理仓库空间)。
二、双指针:两人协作的“高效工作法”
双指针是用两个指针变量协同解决问题的技巧,通过不同的移动规则优化算法效率。以下是三种常见类型:
1. 快慢指针(龟兔赛跑)
原理:
两个指针从同一位置出发,速度不同。例如,快指针每次走两步,慢指针走一步。
应用:
检测链表环:如果链表有环,快指针会绕圈追上慢指针,就像两个人在环形跑道上跑步总会相遇。
找链表中点:快指针到终点时,慢指针刚好在中点,类似“中间截断”。
例子:
# 检测链表是否有环
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next # 乌龟走一步
fast = fast.next.next # 兔子走两步
if slow == fast: # 相遇说明有环
return True
return False
```
2. 对撞指针(左右夹击)
原理:
两个指针从两端向中间移动,像两把夹子逐渐合拢。常用于有序数组问题。
应用:
两数之和:在有序数组中找两个数的和等于目标值,左右指针根据当前和调整位置。
反转字符串:左右指针交换字符,直到中间汇合。
例子:
# 有序数组找两数之和
def two_sum(nums, target):
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
```
3. 分离指针(分工合作)
原理:
两个指针分别操作不同的数据结构,比如一个遍历数组A,另一个遍历数组B。
应用:
合并有序数组:两个指针分别扫描两个数组,按顺序合并。
数组去重:一个指针标记非重复位置,另一个扫描剩余元素。
三、为什么用双指针?
1. 减少时间复杂度:将暴力解法的O(n²)优化为O(n),比如对撞指针避免嵌套循环。
2. 节省空间:无需额外存储结构,直接在原数据上操作。
3. 解决特定问题:如链表环、子数组求和、动态窗口等场景。
总结:
指针是操作内存的“导航”,而双指针通过两人协作(快慢、对撞、分离)实现高效解题,就像快递员精准投递或团队分工合作清理仓库,既快又省力。