leetcode-[數組、雙指針、二分搜索]-尋找重複數(287)

1、問題描述

給定一個包含n+1個整數的數組nums,其中,每個整數的取值範圍爲[1,n],根據鴿巢原理可知,至少存在一個重複的整數。假設只有一個重複的數,找出這個數。

2、解題思路

  • 邊界條件與特殊情況:(1)nums中整數的個數小於2;
  • 問題分析:以nums=[1,3,4,2,3]爲例,索引index的範圍是在[0,n],元素值value的範圍是[1,n],想象一下,把數組看成一個鏈表,數組中每一個位置上的元素值作爲下一個元素的索引,當有元素重複時,會得到下面這樣的帶有環的索引鏈表:
    在這裏插入圖片描述
index value
0 1
1 3
2 4
3 2
4 3
  • 思路1:根據上面的分析,類比判斷鏈表中是否存在環問題的解題思路,採用快慢指針來解決這個問題,整個思路分爲兩個步驟:
  • (1)檢測環。具體地,快、慢指針fast、slow都從0開始,進行如下循環:slow每次進行一跳,而fast進行兩跳,比如,一開始slow=i、fast=i,下一步中,slow=nums[i],而fast=nums[nums[i]]。當slow等於fast,循環停止,此時slow和fast都停在環上某個節點。
  • (2)找到環的入口。具體地,finder指針從0開始,slow從上一步中slow和fast在環中的相遇的開始,進行如下循環:slow和finder指針每次都進行一跳。當finder等於fast時,循環停止,此時finder指向的爲環的入口。
  • 這裏可能有點難理解的是:爲什麼finder和slow會在環的入口處相遇?我們可以證明一下:
  • 假設起點到環的入口處長度爲m,環的周長爲c,在檢測環的過程中,快慢指針在環中相遇,此時,fast走了2n步,slow走了n步,根據以上假設可得出以下結論:
  • 由於兩個指針在環中相遇,所以多走的n步一定是c的整數倍,即n%c=0;
  • fast和slow相遇時,slow在環中行走的距離是n-m,此時,若finder從起點開始,slow從相遇點開始,兩者同步前進,每次一步,當finder走了m步到達環的入口處時,slow走了n-m+m=n步,而n又是c的整數倍,故兩者會在環的入口相遇。
  • 先聲明一下數據結構:
  • slow慢指針、fast快指針,finder指針。
  • 初始化,一開始slow和fast都爲0,finder也爲0
  • 處理邏輯: 檢測環和找到環的入口。
  • 這個方法的時間複雜度爲O(n)O(n),空間複雜度爲O(1)O(1)
  • 思路2:一說到查找,且時間複雜度小於O(n2)O(n^2),則可以嘗試二分查找,二分查找的思路如下:
  • 由於數組的元素的取值範圍在[1,n],所以需要查找的數的區間也left=1、right=n,取該區間的中位數mid,統計數組中小於等於mid的元素個數count,如果count<=mid,則說明重複的整數在[mid+1,right]區間,否則在[left,mid]之間,如果left <right,則繼續在子區間查找。
  • 由於進行了lognlogn次查找,統計小於mid的元素個數count時間複雜度爲O(n)O(n),所以時間複雜度爲O(nlogn)O(nlogn),空間複雜度爲O(1)O(1)

3、代碼實現

快慢指針實現:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
         if (nums.size() < 2){
            return 0;
        }
        
        int slow=0;
        int fast = 0;
        int finder = 0;
        while(true){
            slow = nums[slow];
            fast = nums[nums[fast]];
            if(slow == fast){
                break;
            }

        }

        while(true){
            slow = nums[slow];
            finder = nums[finder];
            if(slow == finder){
                break;
            }
        }
        return finder;
    }
};

二分搜索實現:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
         if (nums.size() < 2){
            return 0;
        }
        int left = 1;
        int right = nums.size() - 1;
        while(left < right){
            int mid = (left + right) /2;
            int count = countNums(nums,mid);
            if(count <= mid){
                left = mid + 1;
            }
            else{
                right = mid;
            }

        }
        return left;
        
    }
    int countNums(vector<int>& nums,int e){
        int count = 0;
        for(int i=0;i<nums.size();i++){
            if(nums[i] <= e){
                count ++;
            }
        }
        return count;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章