算法技巧——雙指針算法

前置知識

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;
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章