1. 程式人生 > 其它 >雙指標技巧秒殺七道陣列題目

雙指標技巧秒殺七道陣列題目

雙指標技巧秒殺七道陣列題目

在處理陣列和連結串列相關問題時,雙指標技巧是經常用到的,雙指標技巧主要分為兩類:左右指標快慢指標

所謂左右指標,就是兩個指標相向而行或者相背而行;而所謂快慢指標,就是兩個指標同向而行,一快一慢。

一、快慢指標技巧

陣列問題中比較常見且難度不高的的快慢指標技巧,是讓你原地修改陣列

力扣第 26 題「 刪除有序陣列中的重複項

我的C++程式碼:

讓慢指標 slow 走在後面,快指標 fast 走在前面探路,找到一個不重複的元素就賦值給 slow 並讓 slow 前進一步。

這樣,就保證了 nums[0..slow] 都是無重複的元素,當 fast 指標遍歷完整個陣列 nums

後,nums[0..slow] 就是整個陣列去重之後的結果。

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int slow=0,fast=1;
        while(fast<nums.size()){
            if(nums[fast]!=nums[slow]){
                slow++;
                nums[slow]=nums[fast];
            }
            fast++;
        }
        return slow+1;
    }
};

演算法執行的過程如下 GIF 圖:

力扣第 83 題「 刪除排序連結串列中的重複元素

與上題思路相同,或許更簡單些?

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head==nullptr)return nullptr;
        ListNode* slow=head;
        ListNode* fast=head;
        while(fast!=nullptr){
            if(fast->val!=slow->val)
            {
                slow->next=fast;
                slow=fast;
            }
            fast=fast->next;
        }
        slow->next=fast;
        return head;
    }
};

演算法執行的過程請看下面這個 GIF:

力扣第 27 題「 移除元素

我的C++程式碼

如果 fast 遇到值為 val 的元素,則直接跳過,否則就賦值給 slow 指標,並讓 slow 前進一步。

注意這裡和有序陣列去重的解法有一個細節差異,我們這裡是先給 nums[slow] 賦值然後再給 slow++,這樣可以保證 nums[0..slow-1] 是不包含值為 val 的元素的,最後的結果陣列長度就是 slow

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow=0,fast=0;
        while(fast<nums.size()){
           if(nums[fast]!=val){
               nums[slow]=nums[fast];
               slow++;

           }
            
            fast++;
        }
        return slow;
    }
};

力扣第 283 題「 移動零

我的C++程式碼:

相當於移除 nums 中的所有 0,然後再把後面的元素都賦值為 0 即可。

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int slow=0,fast=0;
        while(fast<nums.size()){
            if(nums[fast]!=0){
                nums[slow]=nums[fast];
                slow++;
            }
            fast++;
        }
        while(slow<nums.size()){
            nums[slow]=0;
            slow++;
        }
    }
};

二、左右指標的常用演算法

1、二分查詢

框架

int binarySearch(int[] nums, int target) {
    // 一左一右兩個指標相向而行
    int left = 0, right = nums.length - 1;
    while(left <= right) {
        int mid = (right + left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; 
        else if (nums[mid] > target)
            right = mid - 1;
    }
    return -1;
}

2、兩數之和

力扣第 167 題「 兩數之和 II

C++程式碼:

只要陣列有序,就應該想到雙指標技巧。這道題的解法有點類似二分查詢,通過調節 leftright 就可以調整 sum 的大小:

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        //左右雙指標 大了就調整右指標 小了就調整左指標
        int le=0,ri=numbers.size()-1;
        while(le<ri){
            if(numbers[le]+numbers[ri]==target){
                return {le+1,ri+1};
            }
            //和小了
            if(numbers[le]+numbers[ri]<target){
                le++;
            }else{//和大了
                ri--;
            }
        }
        return {le+1,ri+1};
    }
};

3、反轉陣列

一般程式語言都會提供 reverse 函式,其實這個函式的原理非常簡單,力扣第 344 題「 反轉字串」就是類似的需求,讓你反轉一個 char[] 型別的字元陣列

void reverseString(char[] s) {
    // 一左一右兩個指標相向而行
    int left = 0, right = s.length - 1;
    while (left < right) {
        // 交換 s[left] 和 s[right]
        char temp = s[left];
        s[left] = s[right];
        s[right] = temp;
        left++;
        right--;
    }
}

4、迴文串判斷

首先明確一下,迴文串就是正著讀和反著讀都一樣的字串。

比如說字串 abaabba 都是迴文串,因為它們對稱,反過來還是和本身一樣;反之,字串 abac 就不是迴文串。也是兩個指標相向而行即可

力扣第 5 題「 最長迴文子串」:

C++程式碼:

最長迴文子串使用的左右指標和之前題目的左右指標有一些不同:之前的左右指標都是從兩端向中間相向而行,而回文子串問題則是讓左右指標從中心向兩端擴充套件。

class Solution {
public:
    string longestPalindrome(string s) {
        int sz=s.size();
        string res="";
        for(int i=0;i<sz;i++){
            string a1=Palindrome(s,i,i);
            string a2=Palindrome(s,i,i+1);
            res=res.size()>a1.size()?res:a1;
            res=res.size()>a2.size()?res:a2;
        }
        return res;
    }
    string Palindrome(string s,int le,int ri){
        // 防止索引越界
        while (le >= 0 && ri < s.size()&& s[le] == s[ri]) {
        // 雙指標,向兩邊展開
             le--; ri++;
        }
        // 返回以 s[l] 和 s[r] 為中心的最長迴文串
        return s.substr(le+1,ri-le-1);
    }
};