前置知識
C 和 C++ 的數組、指針。
什麼是雙指針
嚴格的來說,雙指針只能說是是算法中的一種技巧。
雙指針指的是在遍歷對象的過程中,不是普通的使用單個指針進行訪問,而是使用兩個相同方向(快慢指針)或者相反方向(對撞指針)的指針進行掃描,從而達到相應的目的。
雙指針問題套路
通俗的說,就是在數組遍歷中,我們使用兩個指針進行操作。所以雙指針問題基本有以下幾個細節:
1、雙指針的初始位置。根據雙指針的分類,有兩種可能。具體看下面的介紹。
2、雙指針的移動方法。根據雙指針的分類,有兩種可能。具體看下面的介紹。
3、遍歷的結束條件。根據雙指針的分類,有兩種可能。具體看下面的介紹。
對撞指針
對撞指針是指在數組中,將指向最左側的索引定義爲左指針(left)
,最右側的定義爲右指針(right)
,然後從兩頭向中間進行數組遍歷。快速排序就是典型的雙指針問題。
我們假設數組名字爲 nums,數組長度爲 n,數組首元素對應的位置爲 0。
代碼細節
指針初始位置
左指針(left)一般指向數組的第一個元素。即 left = 0。
右指針(right)一般指向數組的第一個元素。即 right = n-1。
指針移動方法
左指針(left)向右邊👉移動,
一般每次移動一個位置,即 left++。
右指針(right)向左邊👈移動,
一般每次移動一個位置,即 right--。
結束條件
左指針(left)位置和右指針(right)位置逆序。
從上面的描述可知,開始的時候,right >= left。因此結束的條件就是 right < left。
僞代碼
function fn(int list[], int len) {
int left = 0;
int right = len - 1;
//遍歷數組
while (left <= right) {
left++;
// 一些條件判斷 和處理
... ...
right--;
}
}
例題
我們使用 LeetCode 881 救生艇爲例,https://leetcode-cn.com/problems/boats-to-save-people/。
解題大致思路爲:
1、排序。
2、初始化雙指針。
3、遍歷。每次都用一個最重的和一個最輕的進行配對,如果二人重量小於 Limit
,則此時的最輕的上船,left++
。不管最輕的是否上船,最重的都要上船,right--。
並且所需船數量加一,num++。
AC 參考代碼
class Solution {
public:
int numRescueBoats(vector<int>& people, int limit) {
sort(people.begin(), people.end());//排序
int ans = 0;//答案
int left = 0;//左指針
int right = people.size() - 1;//右指針
while (left<=right) {
if (people[left]+people[right]<=limit) {
left++;
}
right--;
ans++;
}
return ans;
}
};
快慢指針
快慢指針是兩個指針從同一側開始遍歷數組,將這兩個指針分別定義爲快指針(fast)
和慢指針(slow)
,兩個指針以不同的策略移動,直到兩個指針的值相等(或其他特殊條件)爲止,如快指針(fast)
每次增長兩個,慢指針(slow)
每次增長一個。
一般來說,快慢指針常用於判斷鏈表等數據結構中是否有環。比如下圖所示的一個鏈表。
代碼細節
指針初始位置
慢指針(slow)一般指向數組的第一個元素。即 slow = 0。
快指針(fast)一般指向數組的第二個元素。即 fast = 1。
指針移動方法
慢指針(slow)向右邊👉移動,
一般每次移動一個位置,即 slow++。
快指針(fast)向右邊👉移動,
一般每次移動兩個個位置,即 fast += 2。
結束條件
慢指針(slow)位置和快指針(fast)位置重合;快指針(fast)達到數組的最後一個元素。
特別注意,要判斷達到最後一個元素。否則會出現死循環。
僞代碼
注意:僞代碼只是表示一種結構。
function fn(LinkList *list, int len) {
int slow = 0;
int fast = 1;
//遍歷數組
while (slow != fast) {
slow++;
// 一些條件判斷 和處理
... ...
fast+=2;
}
}
例題
我們使用 LeetCode 141 環形鏈表爲例,https://leetcode-cn.com/problems/linked-list-cycle/。
解題大致思路爲:
1、排序。
2、初始化雙指針。
3、遍歷。
AC 參考代碼
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
//特例判斷
if (NULL==head || NULL==head->next) {
return false;
}
ListNode *slow = head;//慢指針
ListNode *fast = head->next;//快指針
while (slow!=fast) {
//判斷快指針是否到尾
if (NULL==fast || NULL==fast->next) {
return false;
}
//修改位置
slow = slow->next;
fast = fast->next->next;
}
return true;
}
};