力扣448 找到所有陣列中消失的數字

2021-10-13 05:28:05 字數 3269 閱讀 7477

給定乙個範圍在 1 ≤ a[i] ≤ n ( n = 陣列大小 ) 的 整型陣列,陣列中的元素一些出現了兩次,另一些只出現一次。

找到所有在 [1, n] 範圍之間沒有出現在陣列中的數字。

您能在不使用額外空間且時間複雜度為o(n)的情況下完成這個任務嗎? 你可以假定返回的陣列不算在額外空間內。

輸入:

[4,3,2,7,8,2,3,1]

輸出: [5,6]

說一下本人怎麼理解本題的。

首先給定陣列長度為n ,正常來說是可以滿足 數字1-n各出現一次的,但是因為陣列nums中有些元素出現了2次,導致一些元素「消失」了,即沒有位置讓它出現了。這就是題幹中「消失的數字」的由來。

然後,本人想到用雜湊的方法來解題,從 1 - n 組成的「對照序列」中刪除那些nums**現過的元素,剩下的即為答案。由於雜湊表每次查詢和刪除的開銷為o(1),所以一套下來的時間複雜度可以保證為o(n)

這裡,我提出一種說法,叫做「對照序列」,即為當給定陣列nums不出現重複元素時,數字 1-n 組成的序列。

本題的雜湊表使用python集合即可。

class

solution

:def

(self, nums: list[

int])-

> list[

int]

:# 獲取n的值

n =len(nums)

# 通過集合 建立「對照序列」

ns =

set(

range(1

, n+1)

)# 遍歷nums,從對照序列中刪除出現過的元素

for i in nums:

ns.discard(i)

# 對照序列中剩下的即為未出現過的「消失的數字」

return ns

值得注意的是,刪除集合元素的方法有好幾個:

1、set.pop() 隨機從集合中刪除乙個元素,顯然不適用於本題

2、set.remove(x) 從集合中刪除指定元素,若不存在(沒找到)會報錯。由於nums中部分元素出現2次,當第二次遍歷到相同元素時,如果使用remove刪除集合元素,那麼就會報錯,因此也不適用該方法。

3、set.discard(x) 從集合中刪除指定元素,若不存在不會報錯。非常適合本題,理由2中已述。

本題的題幹中有要求,能否不使用額外空間解題。而解法1是開闢長度為n的集合,因此不算是最優解。

其實,看了力扣官方的題解或是民間大神的題解,都可以理解這道題,關鍵是他們的題解都是上來直接講正確的做法是怎麼做,卻沒有說如何想到這種做法的,本人認為比起記住正確的做法,培養自己如何解題才是更重要的。

好了,回到本題。題解1中使用到的額外空間是「對照序列」,所以如果我們想不使用額外空間,那麼就必須不使用「對照序列」。但是,對於解本題來說,「對照序列」又是必不可少的。這就陷入乙個很尷尬的境地了。

拋開上述考量,之前遇到的題中,很多都會要求「不使用額外空間」,這些題往往都是提示「直接在原地修改」,那麼本題是不是也可以直接在原地修改呢?或者說,我們必不可少的「對照序列」,是不是也可以在原陣列中找到呢?

我覺得這裡就是解題的關鍵,我們要是能意識到,在給定陣列nums中,隱藏了我們需要的「對照序列」,那麼就可以不使用額外空間解題。

說到這裡應該很多人就反應過來了,我們需要的「對照序列」,完全可以通過列表的索引來代替。其實我們可以把列表這種資料結構看成上下兩部分:

1、上部分:元素值序列

2、下部分:索引序列

這裡列表的下半部分,正是天然的「對照序列」。

好,現在我們有了「對照序列」,那麼如何從對照序列中」刪除「出現過的元素呢?這裡我在參考了力扣理解後想到了這樣的方法:

遍歷nums,把出現過的元素作為索引值進行索引,然後標記索引到的元素。

這樣一來,當再次遍歷nums時,凡是被「標記」過的元素,其索引值都出現過的元素,那麼那麼未「標記」的元素,其索引值就是未出現過的元素了。

這麼說很難理解,配上例子會很好理解:

輸入:

nums = [1, 1, 3]

遍歷nums

1、num = 1 找到nums[0],標記nums[0]的值,這裡可以把nums[0]變為負數,這裡「變負數」不是取反,而是變為元素值絕對值的負數。

此時nums = [-1, 1, 3]

2、繼續遍歷nums,num = 1 , 再次標記nums[0]

此時nums = [-1, 1, 3]

3、繼續遍歷nums,num = 3,標記nums[2],把nums[2]變為負數

此時nums = [-1, 1, -3]

這時我們再看nums列表上部分和下部分的對應關係:

[ -1, 1, -3]

[ 0 ,1, 2]

被標記過的元素都為負數,其對應的索引都是出現過的元素,未被標記過的元素其索引都是未出現的數字。此時未標記元素為1,索引為1,故為出現的元素為1+1 =2

class

solution

:def

(self, nums: list[

int])-

> list[

int]

:# 遍歷陣列,標記出現過的元素

for num in nums:

nums[

abs(num)-1

]=-abs

(nums[

abs(num)-1

])# 題幹提到,返回的陣列不算額外空間

results =

list()

# 再次遍歷,檢視哪些元素被標記過,哪些未標記過

for i in

range

(len

(nums)):

if nums[i]

>0:

1)return results

本題題解2的關鍵是:

1、如何在原地找到解題必不可少的「對照序列」(將列表看作上下兩部分)

2、如何在新的「對照序列」中「刪除」出現過的元素(標記即視為刪除)

本人悟出原地的對照序列時,採用的標記是將元素值統一改為-1,但是這樣會出問題,因為改為-1的元素,就不能用來標記了,所以導致漏解,注意標記的正確姿勢是將元素值改為原值絕對值的負數。

另外,值得吐槽的是,方法2更難想,用到的空間更少,但時間開銷大於方法1。

448 找到所有陣列中消失的數字 力扣

題意理解 給定乙個陣列,數字在1和陣列長度n之間,求陣列中沒有出現的1到n之間數字列表。時間複雜度o n 問題分析 陣列方法1 map 缺點,需要o n 空間 方法2 陣列下標和元素作對映。思想是,利用下標和元素一一對應的關係,可以遍歷一次標記存在的元素,標記的方法是負號 這個方法的優點是不會保證原...

力扣 448 找到所有陣列中消失的數字 C

給定乙個範圍在 1 a i n n 陣列大小 的 整型陣列,陣列中的元素一些出現了兩次,另一些只出現一次。找到所有在 1,n 範圍之間沒有出現在陣列中的數字。您能在不使用額外空間且時間複雜度為o n 的情況下完成這個任務嗎?你可以假定返回的陣列不算在額外空間內。示例 輸入 4 3,2 7,8 2,3...

力扣第448題 找到所有陣列中消失的數字

力扣448.找到所有陣列中消失的數字 給定乙個範圍在 1 a i n n 陣列大小 的 整型陣列,陣列中的元素一些出現了兩次,另一些只出現一次。找到所有在 1,n 範圍之間沒有出現在陣列中的數字。您能在不使用額外空間且時間複雜度為o n 的情況下完成這個任務嗎?你可以假定返回的陣列不算在額外空間內。...