本題同樣讓我們找數組中重複的數字,但是給出了很多限制條件。其實leetcode好像對一些限制條件,無法做出判斷,我們還是自我嚴格要求的好哦。
首先,第一個不可以改變數組,其實這個就是限制了我們排序,如果排序,然後再遍曆一下很快就能找到重複的數字了,時間複雜度O(nlogn),空間複雜度O(1);
其次,空間複雜度O(1),這個限制了我們使用hash尋找,因為hash時間複雜度是O(1),但是空間要O(n);
其次,時間複雜度小於O(n^2),這個限制了我們暴力求解,迴圈兩次也可以得到結果,但是時間複雜度需要O(n^2);
最後,它說數組裡重複的數字可能重複不止兩次,這個限制了我們使用數學方法,也就是如果只有一個數重複了兩次,我們可以等差求和,然後相減就可以得到結果了。
然後,我們回頭看這四種解法,明顯是第三種存在可以最佳化的地方,n^2可以降低到nlogn,一般使用的就是二分法了。找到中間數mid,然後遍曆數組,如果大於中間數的數的個數,那麼說明重複的數字在前半段,我們類似遞迴的思想,再次在前半段執行,同樣如果小於說明在後半段出現。這裡注意我們是和mid比較而不是nums[mid],因為數字固定是在0~n之間的數字。時間複雜度還是O(nlogn),空間複雜度O(1)
class Solution(object): def findDuplicate(self, nums): """ :type nums: List[int] :rtype: int """ left = 0 right = len(nums) - 1 while(left <= right): mid = left + (right - left)/2 count = 0 for i in range(len(nums)): if nums[i] <= mid : count += 1 if count > mid : right = mid - 1 else: left = mid + 1 return left
看到leetcode的提示有two points,這個一直不明白,看到別人的部落格,原貼地址,https://segmentfault.com/a/1190000003817671
這裡講到了相應的原理,如果我們的序列是沒有重複的,例如,[1,2,3]。那麼可以由每一個下標對應一個數字,0->1 1->2 2->3,我們把這種映射關係應用出來,從下標0開始,找到0對應的數字,然後將這個數字作為新的下標找到新的數字,直到超出下界。比如這個例子中我們的路徑就是0->1->2->3。
那麼如果序列是重複的,[1,3,3,2],那麼就會有下標對應數字時會有重複,0->1 {1,2}->3 3->2,那麼我們的路徑就會出現環。 0->1->3->2->3->2…,會出現3 2 這個環,那麼環開始的地方就是重複的數字啦。這時我們利用的是leetcode中Linked List Cycle || 的方法,具體可以參見那篇部落格。
class Solution(object): def findDuplicate(self, nums): """ :type nums: List[int] :rtype: int """ slow = nums[0] fast = nums[nums[0]] while(slow != fast): slow = nums[slow] fast = nums[nums[fast]] fast = 0 while(fast != slow): slow = nums[slow] fast = nums[fast] return slow