雙指針系列-幾道簡單的 Leetcode 題目

Leetcode 面試題 10.01. 合併排序的數組

問題描述

給定兩個排序後的數組 A 和 B,其中 A 的末端有足夠的緩衝空間容納 B。 編寫一個方法,將 B 合併入 A 並排序。

初始化 A 和 B 的元素數量分別爲 m 和 n。

示例:

輸入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6], n = 3

輸出: [1,2,2,3,5,6]
說明:

A.length == n + m

解題報告

一般情況下,合併兩個有序數組時,還需要第三個數組,大小爲兩個有序數組的大小之和。但是此題數組 A 的大小足夠大,所以從兩個有序數組的末尾元素開始處理,從數組 A 的末尾開始填充。

時間複雜度:O(m+n)O(m+n)
空間複雜度:O(1)O(1)

實現代碼

class Solution {
public:
    void merge(vector<int>& A, int m, vector<int>& B, int n) {
        int i=m-1,j=n-1,pos=m+n-1;
        while(j>=0&&i>=0){
            if(A[i]>B[j]) A[pos--]=A[i--];
            else A[pos--]=B[j--];
        }
        while(j >= 0) A[pos--] = B[j--];
    }
};

Leetcode 面試題 02.02. 返回倒數第 k 個節點

問題描述

面試題 02.02. 返回倒數第 k 個節點
實現一種算法,找出單向鏈表中倒數第 k 個節點。返回該節點的值。

注意:本題相對原題稍作改動

示例:

輸入: 1->2->3->4->5 和 k = 2
輸出: 4
說明:

給定的 k 保證是有效的。

解題報告

設置兩個指針 slowfastfast 先走 k 步,然後 slowfast 以相同速度前進,當 fast 到達末尾時,slow 即在倒數第 k 個節點。這種邏輯和 Leetcode 面試題52.兩個鏈表的第一個公共節點【思路巧妙,源自題解區】 有點相似。

時間複雜度:O(n)O(n)
空間複雜度:O(1)O(1)

實現代碼

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int kthToLast(ListNode* head, int k) {
        ListNode* slow=head,*fast=head;
        for(int i=1;i<=k;i++){
            fast=fast->next;
        }
        while(fast){
            slow=slow->next;
            fast=fast->next;
        }
        return slow->val;
    }
};

Leetcode 26. 刪除排序數組中的重複項

問題描述

給定一個排序數組,你需要在 原地 刪除重複出現的元素,使得每個元素只出現一次,返回移除後數組的新長度。

不要使用額外的數組空間,你必須在 原地 修改輸入數組 並在使用 O(1) 額外空間的條件下完成。

示例 1:

給定數組 nums = [1,1,2],

函數應該返回新的長度 2, 並且原數組 nums 的前兩個元素被修改爲 1, 2。

你不需要考慮數組中超出新長度後面的元素。

解題報告

設置兩個指針 iji 指着當前有效的索引,j 則跳過重複元素,將非重複元素依次放到指針 i 所在的位置,然後 i 往後移。

時間複雜度:O(m)O(m)
空間複雜度:O(1)O(1)

需要注意的是索引的判斷,一不小心就容易越界。

實現代碼

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

Leetcode Leetcode 27. 移除元素

問題描述

給你一個數組 nums 和一個值 val,你需要 原地 移除所有數值等於 val 的元素,並返回移除後數組的新長度。

不要使用額外的數組空間,你必須僅使用 O(1) 額外空間並 原地 修改輸入數組。

元素的順序可以改變。你不需要考慮數組中超出新長度後面的元素。

示例 1:

給定 nums = [3,2,2,3], val = 3,

函數應該返回新的長度 2, 並且 nums 中的前兩個元素均爲 2。

你不需要考慮數組中超出新長度後面的元素。

解題報告

和上一題有一點相似之處,設置指針 l 指向數組的頭部,設置指針 r 指向數組的尾部。將 r 指向非 val 的元素,然後將左邊的 val 移到右邊。

while(l<=r) 取等號的原因是:如果數組只有一個元素而且等於 val,會無法刪除該元素。

if(l<=r&&nums[l]==val) 中需要滿足 l<=r 的原因是從 while(l<=r&&nums[r]==val) 這個循環跳出來有可能是 l>r 且此時 r<0

時間複雜度:O(m)O(m)
空間複雜度:O(1)O(1)

實現代碼

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int clipLen=0,l=0,r=nums.size()-1;
        while(l<=r){
            while(l<=r&&nums[r]==val) {
                r--;
                clipLen++;
            }
            if(l<=r&&nums[l]==val){
                nums[l]=nums[r--];
                clipLen++;
            }
            l++;
        }
        return nums.size()-clipLen;
    }
};

總結

指針在移動的時候,一定要注意一些邊界條件,很容易就發生越界錯誤了。

  1. 兩個指針的關係判斷時,是否取等號;
  2. 從循環中跳出時,要全面考慮跳出循環的條件。

參考資料

[1] Leetcode 面試題 10.01. 合併排序的數組
[2] Leetcode 面試題 02.02. 返回倒數第 k 個節點
[3] Leetcode 面試題52.兩個鏈表的第一個公共節點【思路巧妙,源自題解區】
[4] Leetcode 26. 刪除排序數組中的重複項
[5] Leetcode 27. 移除元素

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