LeetCode C++ 41. First Missing Positive【Array】困難

Given an unsorted integer array, find the smallest missing positive integer.

Example 1:

Input: [1,2,0]
Output: 3

Example 2:

Input: [3,4,-1,1]
Output: 2

Example 3:

Input: [7,8,9,11,12]
Output: 1

Note: Your algorithm should run in O(n) time and uses constant extra space.

題意:找到一個無序的數組中不存在的最小的正整數。

思路1:O(nlogn)\text{O(nlogn)}O(n)\text{O(n)} 的差別比較小,可以用下面的方法矇混過關。先排序,後去重,然後從小往大搜索,忽視非正數。

代碼1:O(nlogn)\text{O(nlogn)} 時間和常量空間。

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int len = unique(nums.begin(), nums.end()) - nums.begin(), p = 1;
        for (int i = 0; i < len; ++i) {
            if (nums[i] <= 0) continue;
            else if (nums[i] == p) ++p;
            else break;
        }
        return p;
    }
};

思路2:如果用額外的空間做的話,可以用整數哈希表,從 1 開始,能過的話,是 O(n)\text{O(n)} 時間。但是下面的——發現不可能開這樣大的空間,即使用 bitset ,比如 [2147483647]

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        bitset<20000> bst;
        for (const auto &i : nums) 
            if (i > 0) 
                bst.set(i);
        int i = 1;
        while (bst[i]) ++i;
        return i;
    }
};

不過這裏其實是一時糊塗,除了 <=0 的數外,完全可以拒絕大於數組長度的數,然後將其他的數所指向的位置的 bitset1 。最後檢查即可。

代碼2如下,在上面的代碼中做了一點小修改:

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        bitset<10000> bst;
        for (const auto &i : nums) 
            if (i > 0 && i <= nums.size()) 
                bst.set(i);
        int i = 1;
        while (bst[i]) ++i;
        return i;
    }
};

還有能過的是使用 unordered_set此時不用在乎數據範圍。代碼如下:

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        unordered_set<int> st;
        for (const auto &i : nums) st.insert(i);
        int i = 1;
        while (st.find(i) != st.end()) ++i;
        return i;
    }
};

效率:

執行用時:0 ms, 在所有 C++ 提交中擊敗了100.00% 的用戶
內存消耗:6.9 MB, 在所有 C++ 提交中擊敗了100.00% 的用戶

思路3:既然題目說可以在 O(n)\text{O(n)} 和不使用額外空間的情況下得到答案,那麼只有一種可能,即使用原數組。我們忽視負數、0 、超出數組長度範圍的數(說明它們不是答案),則把另外的數,它們指向的數組位置做標記——不可能取負(以前有的題目可以,但是這裏不可以)或置零——排除這些途徑,唯一的方法就是把這些數交換回原來的位置上

交換的方法也有一定的技巧:

  • 從頭檢查起,如果發現一個數 > 0 && <= nums.size() && nums[i] != nums[nums[i] - 1] ,就將其與 nums[nums[i] - 1] (爲了容納等於數組長度的那個正數) 交換;此時仍指向 i 的位置,繼續對這個值實施上面的過程;直到現在的 nums[i] 已經處於正確位置或屬於逃票——票號 <= 0,或 >= 最大座位號,則終止交換;
  • 然後對 i+1,i+2 位置的元素做同樣的操作。

然後,我們從 1 位置開始檢查,發現值和位置+1不匹配,就返回這個位置的下標+1;如果都匹配,則返回數組長度+1。

其實,這種做法看提示應該能夠想得出來。

Think about how you would solve the problem in non-constant space. Can you apply that logic to the existing space?
We don’t care about duplicates or non-positive integers.
Remember that O(2n) = O(n).

代碼3:

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

效率:

執行用時:0 ms, 在所有 C++ 提交中擊敗了100.00% 的用戶
內存消耗:6.3 MB, 在所有 C++ 提交中擊敗了100.00% 的用戶
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章