時間 O(nlog(n)),空間O(n),按題目中Note「只用O(1)的空間」,照理是過不了的,可是可能判題並無卡空間複雜度,因此也能AC。python
class Solution: # 基本思路爲,將第一次出現的數字 def findDuplicate(self, nums: List[int]) -> int: s = set() for i in nums: a = i in s if a == True: return i else: s.add(i)
時間O(n),空間O(1),思路十分巧妙,可是使用條件比較苛刻。根據題目給出的條件,剛好能用這種解法,這應該也是出題人推薦的解法。指針
注:上面所說的環是指1->2->3->1code
以樣例1爲例:[1,3,4,2,2]blog
0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|
1 | 3 | 4 | 2 | 2 |
以下圖所示,其中2->4->2構成環,入環點爲2
leetcode
由題意分析可知,每一個樣例均可以畫成這樣一張圖,咱們只須要找出圖中的環,並找出入環點,即爲所求的重複數字key,下面都用key表示所求的重複數字。io
以樣例1爲例,圖中出現了5個點0-4,圖中存在5根指針線,5個點5根線,一定存在環。
n個點,點的範圍去0~n-1,n根線,一定存在環。(n-1根線是剛好無環的狀況,本身畫圖可知)table
設置一個慢指針slow,一個快指針fast。slow每次走一步,fast每次走兩步,若是slow與fast能相遇,說明圖中存在環,而且相遇點必定存在於環中。ast
有題意分析中的表可知,key的入度必定大於1,即不止一個點能夠直接到key。而key必定存在於環中,因此key必定爲入環點。樣例1中3,4均可到達2,2的入度2,2爲入環點,即爲所求的key。class
slow和fast相交的點記爲相遇點P。
slow和fast從起點0到相遇點P運行步驟以下:
List
這個相遇點P與起點0到達入環點key的步數 差距爲環L的整數倍,故設置slow2從起點0開始,每次走一步,slow從相遇點P開始,每次走一步,slow和slow2必定會相遇在入環點key。
咱們能夠有一個小小的證實,以下圖
設起點0到達入環點key的步數爲x,相遇點P到達入環點key的步數爲y。
設slow指針走到相遇點P的步數爲t,fast走到相遇點P的步數爲2*t。
設走完環一圈的步數爲L
2 * t - x + y = M * L(一)
t - x + y = N * L (二)
fast指針在環中走的步數2t-x,此時到達相遇點P,key->P->key步數爲2t-x+y = M * L,正好爲L的M倍,M爲常數。(一)式
slow指針在環中走的步數t-x,此時到達相遇點P,key->P->key步數爲t-x+y = N * L,正好爲L的N倍,N爲常數。(二)式
2倍(二)式 減 (一)式
y-x = (2N-M) * L
因此y與x的步數差距爲L倍的環。
得證。
假設存在不包含key的環,起點0在不包含key的環中繞圈。
|0|a1|a2|a3|a4|a5|a6|
|-|-|-|-|-|-|-|
|b1|b2|b3|b4|b5|b6|b7|
按題意不包含環,b[i]與b[j]必定不相等(i != j)
因爲b1~b7從1開始,因此b[i]只能從a[j]中取(1<=i<=7,1<=j<=6)
從6個數字的集合a中取7個數字,因此假設不成立,一定存在相同數字b[k],即爲key。
代碼以下
class Solution: def findDuplicate(self, nums: List[int]) -> int: # 若是隻有兩個元素,第一個元素必定是重複元素 if len(nums) == 2: return nums[0] # fast每次走兩步,slow每次走一步,起始點能夠爲任意位置 fast = 0 slow = 0 # python沒有do while,因此在循環外寫了一遍 slow = nums[slow] fast = nums[nums[fast]] while slow != fast: slow = nums[slow] fast = nums[nums[fast]] # fast從起點每次走一步,必定會與slow相遇,此時slow可能在環中走了多倍的L步。 # L爲環一圈的步數 fast = 0 while fast != slow: slow = nums[slow] fast = nums[fast] return fast