第乙個缺失的整數

2021-09-08 21:05:32 字數 3739 閱讀 2008

第乙個缺失的整數

給定乙個長度為n的陣列a[0,…n-1],從1開始,找到第乙個不在陣列中的正整數。

如 [3,9,8,1,6,32]輸出為2。

思路:

求解該問題可以有三種策略,分別是暴力破解法、bitmap法和迴圈不變式方法。

方法1—暴力破解法:

看到這個題後,既然是找從1開始第乙個不在陣列a中的正整數,那最簡單最直接的反應就是我能否可以從1開始,在a中查詢這個數是否存在,若存在則找下乙個正整數,知道找到第乙個不在a中的正整數。可以很明顯可知,該演算法的時間複雜度為o(n

2)

o(n^2)

o(n2

),空間複雜度為o(1

)o(1)

o(1)

,顯然這不是我們想要的結果。

那麼,我們進一步想,如果a是有序的話,則從頭開始查詢,找到第乙個a[i]!=i(為簡單講解,假設陣列的下標是從1開始,下同)的位置,則i即為要找的數字。因為給定的陣列a並不有序,則需要首先對a進行排序,然後在對a進行遍歷即可。則很明顯,該演算法時間複雜度主要花在對a的排序上,即使利用時間複雜度最低的排序演算法,其時間複雜度仍為o(n

∗log

n)

o(n*logn)

o(n∗lo

gn),空間複雜度為o(1

)o(1)

o(1)

.由上可知,暴力破解法的時間複雜度太高。

方法2—bitmap法:

所謂的bitmap就是用乙個bit位來標記某個元素所對應的value,而key即是該元素。由於題目中並沒有對演算法的空間複雜度進行要求,所以我們可以進行以下操作:

首先申請乙個與a

aa長度一樣的空陣列b=[

1,..

.,n]

b=[1,...,n]

b=[1,.

..,n

] (假設陣列b

bb下標是從1

11開始),並將所有位置初始化為−1-1

−1。然後從頭遍歷a

aa,將a[i

]a[i]

a[i]

對映到b

bb中,具體對映方式為如果a[i

]<

1a[i]<1

a[i]

<

1 或者a[i

]>

na[i]>n

a[i]

>

n則捨棄,否則b[a

[i]]

=a[i

]b[a[i]]=a[i]

b[a[i]

]=a[

i]。對映完成後,從1

11開始遍歷陣列b

bb,找到第乙個b[i

]!=i

b[i]!=i

b[i]!=

i的位置,i

ii即為所求。

然而這是為什麼呢?為什麼可以這樣做?

這是因為題目是從1開始找到第乙個不在陣列中的正整數,因為乙個蘿蔔乙個坑,只要a陣列中存在乙個數大於n或者小於1,那麼可以很容易得出這個缺失的正整數z

zz一定滿足z

>=1

並且

z<=n

z>= 1 並且 z<=n

z>=1

並且z<=n

或者如果a

aa陣列中的數都介於1

11和n

nn之間且互不重複,則z必等於n+1

n+1n+

1所以,上述對映和查詢方式是成立的。

bitmap法花費的總時間為2n2n

2n,空間大小為n

nn,因此bitmap演算法的時間複雜度為o(n

)o(n)

o(n)

,空間複雜度也為o(n

)o(n)

o(n)

方法3—迴圈不變式法:

因為bitmap的時間複雜度已經是o(n

)o(n)

o(n)

數量級,對於此類查詢演算法已經不可能有數量級上的優化,即使優化也只是降低係數。但是bitmap演算法的空間複雜度也是o(n

)o(n)

o(n)

,那這個空間複雜度是否可以降低呢?

其實,歸根到底,上述bitmap方法只是在做一件事,那就是將每乙個數字都放到它應該在的位置上去。既然是這樣,我們其實可以不用申請空間,而直接對原陣列進行操作。即從前向後遍歷陣列a,將a中的每乙個數放到對的位置,知道某乙個位置的資料一直沒有被找到,那麼這個位置上的資料即為所求,上面這句話比較不好理解,那我們形式化表示一下:

假定前i−1

i-1i−

1個數已經找到,並且依次存放在a[1

,2,.

....

,i−1

]a[1,2,.....,i-1]

a[1,2,

....

.,i−

1]中,繼續考察a[i],有三種情況:

(1) 若a[i

]a[i]

a[i]

[ i]

>=1

[i]>=1

[i]>=1

,則a[i]在a[1,2,…,i-1]中已經出現過,可以直接丟棄。

ps:若a[i]為負,則更應該丟棄它。

(2)若a[i]>i且a[i]<=n,則a[i]應該位於後面的位置上,則將a[a[i]]和a[i]交換。

ps:若a[i]>=n,由於缺失資料一定小於n,則丟棄a[i]。

(3)若a[i]=i,則a[i]位於正確的位置上,則i加1,迴圈不變式擴大,繼續比較後面的元素。

(1) 若a[i]n,則丟棄a[i]

(2) 若a[i]>i,則將a[a[i]]和a[i]交換。

(3) 若a[i]=i,則i加1,繼續比較後面的元素。

至此,整個的演算法描述已經清楚了。

那麼現在有個問題,即如何對a[i]進行丟棄呢?

倒是可以直接多陣列中這個位置進行刪除,然後後面的所有元素往前移動,不過這樣子也太費時間了。

我們可以參考堆排序或者樹的剪枝操作,直接用最後乙個元素a[a.size()-1]去替換a[i],然後a的長度減1。這樣子就相當於對a[i]進行了丟棄,並且也不需要進行大量的其它操作,就是進行乙個邏輯刪除。

# -*- coding: utf-8 -*-

"""created on sun jan 20 15:04:13 2019

@author: shuaifeng

"""def findfirstmissinginteger(tablea):

if tablea is none or len(tablea)==0:

return 1

endindex=len(tablea)

index=0

while indexendindex or tablea[index]index+1: #將數值放到正確的位置上去

temp=tablea[tablea[index]-1]

tablea[tablea[index]-1]=tablea[index]

tablea[index]=temp

else: #當前位置的資料在正確位置,則查詢下乙個

index=index+1

return index+1

def main():

a=[5,1,1,9,4,10]

print(findfirstmissinginteger(a))

if __name__=="__main__":

main()

第乙個缺失的正整數 leetcode

為什麼不能nums這樣賦值呢?nums nums i nums i 呢 例如 如果陣列中存在3 那麼我們可不可以nums 3 3呢?這樣最後的結果會不會和 0,1,2,3,4,5 嗎?然後對這個陣列從1開始遍歷不就好了嗎這樣做事不行的,因為 1,2,3,0 這樣最後的結果就是 1,1,0,3 因為值...

Python 第乙個缺失的正整數

給定乙個陣列a 0 n 1 找到從1開始,第乙個不在陣列中的正整數。如3,5,1,2,3,7,14,8輸出4 思路 將找到的元素放到正確的位置上,如果最終發現某個元素一直沒有找到,則該元素即為所求。迴圈不變式 如果某命題初始為真,且每次更改後仍然保持該命題為真,則若干次更改後該命題仍然為真。為表述方...

LeetCode 41 缺失的第乙個整數

給定乙個未排序的整數陣列,找出其中沒有出現的最小的正整數。說明 你的演算法的時間複雜度應為o n 並且只能使用常數級別的空間。示例示例 1 輸入 1,2,0 輸出 3 示例 2 輸入 3,4,1,1 輸出 2 示例 3 輸入 7,8,9,11,12 輸出 1 題解 一拿到題就想到用雜湊了,但是空間不...