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% 的用户
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章