LeetCode 287. Find the Duplicate Number (时间复杂度O(n)) + 链表判断环
LeetCode 287. Find the Duplicate Number
暴力解法
時間 O(nlog(n)),空間O(n),按題目中Note“只用O(1)的空間”,照理是過不了的,但是可能判題并沒有卡空間復(fù)雜度,所以也能AC。
class Solution:# 基本思路為,將第一次出現(xiàn)的數(shù)字def findDuplicate(self, nums: List[int]) -> int:s = set()for i in nums:a = i in sif a == True:return ielse:s.add(i)雙指針判斷環(huán)
時間O(n),空間O(1),思路十分巧妙,但是使用條件比較苛刻。根據(jù)題目給出的條件,恰好能用這種解法,這應(yīng)該也是出題人推薦的解法。
題意分析:
注:上面所說的環(huán)是指1->2->3->1
以樣例1為例:[1,3,4,2,2]
| 1 | 3 | 4 | 2 | 2 |
如下圖所示,其中2->4->2構(gòu)成環(huán),入環(huán)點為2
解題思路
由題意分析可知,每個樣例都可以畫成這樣一張圖,我們只需要找出圖中的環(huán),并找出入環(huán)點,即為所求的重復(fù)數(shù)字key,下面都用key表示所求的重復(fù)數(shù)字。
為什么必定存在環(huán)
以樣例1為例,圖中出現(xiàn)了5個點0-4,圖中存在5根指針線,5個點5根線,必定存在環(huán)。
n個點,點的范圍去0~n-1,n根線,必定存在環(huán)。(n-1根線是恰好無環(huán)的情況,自己畫圖可知)
找環(huán)的方法
設(shè)置一個慢指針slow,一個快指針fast。slow每次走一步,fast每次走兩步,如果slow與fast能相遇,說明圖中存在環(huán),并且相遇點一定存在于環(huán)中。
為什么key一定為入環(huán)點?
有題意分析中的表可知,key的入度一定大于1,即不止一個點可以直接到key。而key一定存在于環(huán)中,所以key一定為入環(huán)點。樣例1中3,4都可到達(dá)2,2的入度2,2為入環(huán)點,即為所求的key。
怎么找入環(huán)點key?
slow和fast相交的點記為相遇點P。
slow和fast從起點0到相遇點P運行步驟如下:
這個相遇點P與起點0到達(dá)入環(huán)點key的步數(shù) 差距為環(huán)L的整數(shù)倍,故設(shè)置slow2從起點0開始,每次走一步,slow從相遇點P開始,每次走一步,slow和slow2一定會相遇在入環(huán)點key。
我們可以有一個小小的證明,如下圖
設(shè)起點0到達(dá)入環(huán)點key的步數(shù)為x,相遇點P到達(dá)入環(huán)點key的步數(shù)為y。
設(shè)slow指針走到相遇點P的步數(shù)為t,fast走到相遇點P的步數(shù)為2*t。
設(shè)走完環(huán)一圈的步數(shù)為L
2 * t - x + y = M * L(一)
t - x + y = N * L (二)
fast指針在環(huán)中走的步數(shù)2t-x,此時到達(dá)相遇點P,key->P->key步數(shù)為2t-x+y = M * L,正好為L的M倍,M為常數(shù)。(一)式
slow指針在環(huán)中走的步數(shù)t-x,此時到達(dá)相遇點P,key->P->key步數(shù)為t-x+y = N * L,正好為L的N倍,N為常數(shù)。(二)式
2倍(二)式 減 (一)式
y-x = (2N-M) * L
所以y與x的步數(shù)差距為L倍的環(huán)。
得證。
如何確定起點0一定會進(jìn)入包含key的環(huán)?
假設(shè)存在不包含key的環(huán),起點0在不包含key的環(huán)中繞圈。
| b1 | b2 | b3 | b4 | b5 | b6 | b7 |
按題意不包含環(huán),b[i]與b[j]一定不相等(i != j)
由于b1~b7從1開始,所以b[i]只能從a[j]中取(1<=i<=7,1<=j<=6)
從6個數(shù)字的集合a中取7個數(shù)字,所以假設(shè)不成立,必定存在相同數(shù)字b[k],即為key。
代碼如下
class Solution:def findDuplicate(self, nums: List[int]) -> int:# 如果只有兩個元素,第一個元素一定是重復(fù)元素if len(nums) == 2:return nums[0]# fast每次走兩步,slow每次走一步,起始點可以為任意位置fast = 0slow = 0# python沒有do while,所以在循環(huán)外寫了一遍slow = nums[slow]fast = nums[nums[fast]]while slow != fast:slow = nums[slow]fast = nums[nums[fast]]# fast從起點每次走一步,一定會與slow相遇,此時slow可能在環(huán)中走了多倍的L步。# L為環(huán)一圈的步數(shù)fast = 0while fast != slow:slow = nums[slow]fast = nums[fast]return fast總結(jié)
以上是生活随笔為你收集整理的LeetCode 287. Find the Duplicate Number (时间复杂度O(n)) + 链表判断环的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3遍历选中文件夹下的文件【G
- 下一篇: 【读书笔记】2015年考研英语二真题翻译