[LeetCode 41] 用原數組保存信息

首先這題一個很明顯的性質:缺少的那個整數一定在[1, n + 1](n爲nums的長度)之間。爲什麼?假設這個數組是從1開始,然後依次遞增並且不重複,全部填充時數組正好是[1, n],此時缺少的整數就是n + 1;然後前面替換掉哪個數,哪個數就是缺少的,在[1, n]之間。

所以,可以想到一個很自然的解法,用一個數組來記錄[1, n]之間哪個數已經出現過了,沒出現的那個就是缺少的;如果全都出現了,那就是缺少了n + 1。這個解法我認爲比哈希表還要自然:

int firstMissingPositive(vector<int> &nums)
{
    int length = nums.size();
    vector<bool> lookup(length, false);
    for (int &n : nums)
    {
        if (1 <= n && n <= length)
        {
            lookup[n] = true;
        }
    }
    for (int i = 1; i <= length; ++i)
    {
        if (!lookup[i])
        {
            return i;
        }
    }
    return length + 1;
}

雖然這個方法可以滿足O(n)的時間複雜度,但題目要求是空間複雜度O(1),這個方法的空間複雜度是O(n),還需要進一步優化。

仔細想想,這個方法的本質是什麼,記錄[1, n]之間已經出現過的數。目前我們是開了個新數組,但是這個數組其實可以不用開,我們完全可以在原數組上記錄,只需要用符號來表明這個位置對應的數是否已經出現過即可。如果當前位置的數爲正,說明當前位置對應的數沒出現過;反之則是已經出現過。當然,這個可能會和原有的負數衝突,所以就需要把原有的負數變成不影響結果的正數(並且負數本身就不影響結果):

int firstMissingPositive(vector<int> &nums)
{
    int length = nums.size();
    for (int &n : nums)
    {
        if (n <= 0)
        {
            n = length + 1;
        }
    }
    for (int i = 0; i < length; ++i)
    {
        int index = abs(nums[i]) - 1;
        if (index < length && nums[index] > 0)
        {
            nums[index] *= -1;
        }
    }
    for (int i = 0; i < length; ++i)
    {
        if (nums[i] > 0)
        {
            return i + 1;
        }
    }
    return length + 1;
}

事實上,用原數組保存信息這種思路還是比較常見的。

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