玩转双指针
一、算法简介
双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多
个数组的多个指针。
若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的
区域即为当前的窗口),经常用于区间搜索。
若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是
排好序的。
二、指针小知识
对于C++ 语言,指针还可以玩出很多新的花样。一些常见的关于指针的操作如下
(1)指针与常量
简而言之,就是const修饰谁,谁就是常量,这里把“*”(星号),理解为独立的字符——指针,就比较好理解了
i. const int * p2, 为常量指针(先读const——常量,再读*——指针),即指向一个常量(const int)的指针。
ii. int * const p3,为指针常量(先读*——指针,再const——常量),指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容(值)可以变化。
(2)指针函数与函数指针
#include <iostream> // addition是指针函数,一个返回类型是指针的函数 int* addition(int a, int b) { int* sum = new int(a + b); // 注意,在函数内new出来的,出函数是不会自动销毁的,一定要在某处通过delete手动销毁。 return sum; } int subtraction(int a, int b) { return a - b; } int operation(int x, int y, int (*func)(int, int)) { return (*func)(x,y); } // minus是函数指针,指向函数的指针 int (*minus)(int, int) = subtraction; int main() { int* m = addition(1, 2); // 1 + 2 = 3 int n = operation(3, *m, minus); // 3 -3 = 0 std::cout << *m << std::endl; std::cout << n << std::endl; return 0; }
输出结果为:
三、例题
因为数组已经排好序,我们可以采用方向相反的双指针来寻找这两个数字,一个初始指向最
小的元素,即数组最左边,向右遍历;一个初始指向最大的元素,即数组最右边,向左遍历。
Leetcode 88. 合并两个有序数组
Leetcode 142. 环形链表 II
对于链表找环路的问题,有一个通用的解法——快慢指针(Floyd 判圈法,其有数学证明)。
给定两个指针,分别命名为slow 和fast,起始位置在链表的开头。(步骤1) 每次fast 前进两步,slow 前进一步。
如果fast可以走到尽头,那么说明没有环路;如果fast 可以无限走下去,那么说明一定有环路,且一定存
在一个时刻slow 和fast 相遇。(步骤2)当slow 和fast 第一次相遇时,我们将fast 重新移动到链表开头,并
让slow 和fast 每次都前进一步。(步骤3)当slow 和fast 第二次相遇时,相遇的节点即为环路的开始点。
该算法可以:
- 判断链表是否有环
- 计算环的长度
- 寻找环的起点
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode *fast = head, *slow = head; // 步骤1 do { if (!fast || !fast->next) return nullptr; fast = fast->next->next; slow = slow->next; } while (fast != slow); // 步骤2 fast = head; while (fast != slow) { fast = fast->next; slow = slow->next; } // 步骤3 return fast; } };