首先這題一個很明顯的性質:缺少的那個整數一定在[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;
}
事實上,用原數組保存信息這種思路還是比較常見的。