[leetcode] findDuplicateNumber

FindDuplicateNumber

  • 問題描述:給定一個數組長度爲N,裏面元素的取值範圍是1~N-1。所有會有一個數是重複的,請找到這個重複的數字並返回。
  • 解法1:暴力
    • 最直接的辦法就是利用兩重循環來尋找
    • 當碰到相同的數字的時候就返回
    • 時間複雜度是O(N)
  • 解法2: 二分法
    • 我們知道我們N個數是分佈在1~N-1之間的。所有假設我們統計1~N/2, N/2-N-1之間的數字,如果哪一方的數字多餘一半,則重複的數字就在其中。
    • 通過這樣的方式我們可以二分的查找多餘的數字
    • 時間複雜度是O(NlogN)
  • 解法3:
    • 我們嘗試構造下面的數列:
      • X0=nums[0]X_0 = nums[0]
      • X1=nums[X0]X_1 = nums[X_0]
      • X2=nums[X1]X_2 = nums[X_1]
      • Xi=nums[Xi1]X_i = nums[X_{i-1}]
    • 因爲我們數字裏存在重複的數字,所有一定存在一個XiX_i[X0,Xi1][X_0,X_{i-1}]之中出現過。形成如下所示的帶環鏈表。
      cycle linked list
    • 接下來我們就是要找到入口前的一個點,他就是我們重複的數字。
    • 那麼我們如何找呢?
      • 我們先利用兩個point,一快一慢,快的走兩步,慢的走一步。
      • 因爲我們存在環,所有兩個指針一定會相遇。
      • 假設兩個相遇在某一點,則此時再讓fast從頭開始,然後slow和fast都每次走一步。
      • 兩者相遇的時候就是我們的重複的數字
    • why?
      • 假設一開始兩者相遇的時候slow的走了N步,則fast的走了2N步
      • 接下來我們分析一下這N步是由什麼構成的?
        • 首先在進環前走了D步
        • 從環的起點到相遇點走了K步。
        • 整個環走了i圈,假設環的長度是C
        • 則N = D + K + C*i
      • 那麼對於2N,我們同樣有2N = D + K + C*j
      • 我們消去N可以得到2D + 2K + 2Ci = D + K + Cj =>D+K = C(j-i). 注意j>i是成立的。
      • 所有我們讓一個point從頭走D步。
      • 那麼從相遇的點相當於走D=C(j-i)-K。
      • 相當於我們差K個step可以走滿C(j-i+1)圈。
      • 因爲我們相遇的點距離環的入口點是K。所有此時一定是相遇在起點的。
    • 上面我們分析了爲什麼可以相遇在起點。又因爲我們兩個point在經歷起點前的一個點也一定是相同的,參照上圖。
    • 所以我們可以得到當第一次兩個相同的時候,就是我們的重複數字。
    • 如果說上面方法難以理解的話,我們可以在中加加上一步
      • 找到相遇的點
      • 計算環的長度L
      • 初始化兩個point
      • 第一個point先走L步
      • 然後兩個point同時走,當兩個point相遇的時候就是環的入口點。
  • 三種方法的代碼:
//
// Created by 樑棟 on 2019-05-16.
//
#include <vector>
#include <iostream>
using namespace std;
class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int size = nums.size();
        for(int i=0;i<size;i++){
            for(int j=i+1;j<size;j++)
                if(nums[i] == nums[j])
                    return nums[i];
        }
        return -1;
    }
    int findDuplicateV2(vector<int>& nums){
        int size = (int) nums.size();
        if(size > 1){
            int slow = nums[0];
            int fast = nums[nums[0]];
            while(slow != fast){
                slow = nums[slow];
                fast = nums[nums[fast]];
            }
            slow = 0;
            while(slow != fast){
                slow = nums[slow];
                fast = nums[fast];
            }
            return slow;
        }
        return -1;
    }
    int findDuplicateV3(vector<int>& nums){
        int size = (int) nums.size();
        if(size > 1){
            int leftCount = 0;
            int rightCount = 0;
            int left = 1;
            int right = size - 1;
            while(left < right){
                int mid = (right + left) / 2; //
                int count = 0;
                for(auto c: nums){
                    if(c <= mid){
                        count += 1;
                    }
                }
                if(count > mid){
                    right = mid;
                }else{
                    left = mid + 1;
                }
            }
            return left;
        }
        return -1;
    }
    static void solution(){
        Solution solution1;
        vector<int> nums = {1, 3, 4, 2, 2};
        cout<<solution1.findDuplicateV3(nums)<<endl;
    }
};

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章