41. 缺失的第一個正數

困難 數組 哈希表 雙指針

先是有對題目的分析,才用雙指針實現它

思路

題目額外要求時間複雜度 O(n)O(n) ,空間複雜度 O(1)O(1) 。假設現在數組如下

nums32185423513index0123456789\begin{array}{r|cccccccccc} \text{nums} & -3 & 2 & 1 & 8 & 5 & 4 & 2 & 3 & 5 & 13 \\ \hline \text{index} & 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \end{array}

要求缺失的第一個正數,對於大小為 10 的數組來說,最佳的情況,就是包含 1101\sim10 的所有數字。
對於位置 00 來說,應該要把數字 11 放在這裡 \to 對於位置 ii 來說,應該要把 i+1i+1 放在這裡。


創建兩個指針left = 0right = 9,分情況討論,指針的移動規則如下,從上往下順著判斷:
left左邊的所有位置都是擺放正確的,而right右邊放著多餘的,不需要的數值。

  1. nums[left] == left + 1
    • 符合期望,left++
  2. nums[left] <= left
    • 不符合期望,right--,交換nums[left], nums[right]
  3. nums[left] > right
    • 不符合期望,right--,交換nums[left], nums[right]
  4. nums[nums[left] - 1] == nums[left]
    • 雖然是範圍內的數字,但是目標位置已經有正確的數字擺放,這個數字是多餘的,right--,交換nums[left], nums[right]
  5. nums[nums[left] - 1] != nums[left]
    • 沒有正確的數字擺放,這個數字要交換到正確的位置上,交換nums[left], nums[nums[left] - 1]

※ 條件二三是相同的,都是遇到了預期範圍之外的數字。

詳細流程

  1. 滿足第二個條件,right--,交換nums[left], nums[right]3-3 是一個沒有落在期望範圍 1101\sim10 的數字,因為多了一個無用的數字,所以期望範圍縮減成 191\sim9
nums32185423513index012345678910nums13218542353index0123456789\begin{array}{r|cccccccccc} \text{nums} & -3 & 2 & 1 & 8 & 5 & 4 & 2 & 3 & 5 & 13 \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & \orange{10} \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & \orange{13} & 2 & 1 & 8 & 5 & 4 & 2 & 3 & 5 & \orange{-3} \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & \orange9 \end{array}
  1. 滿足第一個條件,交換nums[left], nums[right]right--, 多了一個無用數字 1313 ,期望範圍縮減, 181\sim8
nums13218542353index0123456789nums52185423133index0123456789\begin{array}{r|cccccccccc} \text{nums} & 13 & 2 & 1 & 8 & 5 & 4 & 2 & 3 & 5 & -3 \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & \orange9 \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & \orange{5} & 2 & 1 & 8 & 5 & 4 & 2 & 3 & \orange{13} & -3 \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & \orange8 & 9 \end{array}
  1. 滿足第四個條件,出現的數字在期望範圍內,但是對應的位置已經放了正確的數字(藍色標記),所以當前的數字無用,交換nums[left], nums[right]right--,期望範圍縮減, 171\sim7
nums52185423133index0123456789nums32185425133index0123456789\begin{array}{r|cccccccccc} \text{nums} & 5 & 2 & 1 & 8 & 5 & 4 & 2 & 3 & 13 & -3 \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & \orange8 & 9 \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & \orange{3} & 2 & 1 & 8 & 5 & \blue4 & 2 & \orange5 & 13 & -3 \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & \blue5 & 6 & \orange7 & 8 & 9 \end{array}
  1. 滿足第五個條件,出現的數字在期望範圍內,而且對應的位置沒有正確數字,交換到正確位置上面。
nums32185425133index0123456789nums12385425133index0123456789\begin{array}{r|cccccccccc} \text{nums} & 3 & 2 & 1 & 8 & 5 & 4 & 2 & 5 & 13 & -3 \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & 5 & 6 & \orange7 & 8 & 9 \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & \orange1 & 2 & \orange3 & 8 & 5 & 4 & 2 & 5 & 13 & -3 \\ \hline \text{index} & \orange0 & 1 & \orange2 & 3 & 4 & 5 & 6 & \orange7 & 8 & 9 \end{array}
  1. 連續三次滿足第一個條件,數字都在正確的位置上,移動左指針,此時期望範圍 474\sim7
nums12385425133index0123456789nums12385425133index0123456789\begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & 8 & 5 & 4 & 2 & 5 & 13 & -3 \\ \hline \text{index} & \orange0 & 1 & 2 & 3 & 4 & 5 & 6 & \orange7 & 8 & 9 \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & 8 & 5 & 4 & 2 & 5 & 13 & -3 \\ \hline \text{index} & 0 & 1 & 2 & \orange3 & 4 & 5 & 6 & \orange7 & 8 & 9 \end{array}
  1. 滿足第三個條件,出現的數字在範圍外,交換nums[left], nums[right]right--, 期望範圍縮減, 464\sim6
nums12385425133index0123456789nums12325485133index0123456789\begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & 8 & 5 & 4 & 2 & 5 & 13 & -3 \\ \hline \text{index} & 0 & 1 & 2 & \orange3 & 4 & 5 & 6 & \orange7 & 8 & 9 \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & \orange2 & 5 & 4 & \orange8 & 5 & 13 & -3 \\ \hline \text{index} & 0 & 1 & 2 & \orange3 & 4 & 5 & \orange6 & 7 & 8 & 9 \end{array}
  1. 滿足第二個條件,出現的數字在範圍外,交換nums[left], nums[right]right--, 期望範圍縮減, 454\sim5
nums12325485133index0123456789nums12345285133index0123456789\begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & 2 & 5 & 4 & 8 & 5 & 13 & -3 \\ \hline \text{index} & 0 & 1 & 2 & \orange3 & 4 & 5 & \orange6 & 7 & 8 & 9 \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & \orange4 & 5 & \orange2 & 8 & 5 & 13 & -3 \\ \hline \text{index} & 0 & 1 & 2 & \orange3 & 4 & \orange5 & 6 & 7 & 8 & 9 \end{array}
  1. 連續兩次滿足第一個條件,移動左指針,此時兩指針重疊,最後的答案是left + 1(5+1)
nums12345285133index0123456789nums12345285133index0123456789left, right\begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & 4 & 5 & 2 & 8 & 5 & 13 & -3 \\ \hline \text{index} & 0 & 1 & 2 & \orange3 & 4 & \orange5 & 6 & 7 & 8 & 9 \end{array} \to \begin{array}{r|cccccccccc} \text{nums} & 1 & 2 & 3 & 4 & 5 & 2 & 8 & 5 & 13 & -3 \\ \hline \text{index} & 0 & 1 & 2 & 3 & 4 & \orange5 & 6 & 7 & 8 & 9 \\ &&&&&&\text{left, right} \end{array}

程式碼

雙指針

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        int left = 0, right = n; // 左閉右開
        while(left < right) {
            if(nums[left] == left + 1) {
                left++;
            }
            // 條件二三四
            else if(nums[left] <= left || nums[left] > right || nums[nums[left] - 1] == nums[left]) {
                swap(nums[left], nums[--right]);
            }
            else {
                swap(nums[left], nums[nums[left] - 1]);
            }
        }
        return left + 1;
    }
};

複雜度分析

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

顯示設定

背景線條
顯示背景網格線條
懸停發光
滑鼠懸停時顯示霓虹效果
聚光燈
跟隨滑鼠的聚光燈效果
背景透明度
開啟透明玻璃效果
主題顏色
自訂主要顏色