【每日一題】LeetCode. 287. 尋找重複數

每日一題,防止癡呆 = =

一、題目大意

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。
在這裏插入圖片描述
在這裏插入圖片描述
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/find-the-duplicate-number

二、題目思路以及AC代碼

這道題剛看到的時候以爲是用異或做了 = =,結果發現題目中說重複數的重複次數可能大於1次,這用異或就沒有辦法了,主要還是參照題解學習了三種解題方式。

思路一:二分

題目要求時間複雜度小於O(n2),所以無非就是O(nlogn)和O(n),一看到O(nlogn),我首先是想到快排,然後稀裏糊塗就給過了(其實題目要求不能修改原數組的),所以就可以考慮二分。

但是對於二分,我們需要構造單調序列,這裏可以考慮單調序列cnt,其中cnt[i]表示在nums數組中,小於等於i的數的個數,我們用target表示我們要找的重複數,那麼可以想象當i < target的時候,cnt[i] 是小於等於i的(當且僅當重複數沒有佔用小於target數的位置時等號成立),當i >= target的時候,cnt[i]是大於i的,所以我們就可以根據以上情況進行二分,即如果cnt[mid] <= mid,那麼target一定在右側的數組中,如果cnt[mid] > mid,那麼target一定在左側的數組中或者就是mid.

按道理來說,應該先計算cnt數組,複雜度是O(n),然後再二分,複雜度是O(logn),總體複雜度O(n),但是由於題目要求不能使用額外的存儲空間,所以這裏就得現算cnt,造成結果的複雜度是O(nlogn).

思路二:二進制

和思路一差不多,這裏的思路是考慮確定重複數的各個二進制位。即我們考慮重複數的第i位是否是1,我們令x表示nums數組中,第i位爲1的總個數,y表示1~n中,第i位爲1的總個數。那麼如果x > y,那該位就是1,如果x <= y,那麼該位就是0.

我們可以分情況考慮上述的問題。重複數我們分爲重複1次,和重複多次。如果是重複一次,那麼當重複數第i位爲1的時候,則x = y + 1,當重複數爲0的時候,則x = y,滿足上述提到的條件。然後當重複數是重複多次的時候,必定要替代一個原來的數B,如果重複數第i位爲1,B第i位爲0,那麼x > y+1,如果重複數第i位爲1,B第i位爲1,那麼x = y+1 > y,如果重複數第i位爲0,B第i位爲1,那麼x < y,如果重複數第i位爲0,B第i位爲0,那麼x = y,綜上所述,都滿足上述提到的判定重複數第i位值得條件,即如果x > y,那該位就是1,如果x <= y,那麼該位就是0.

思路三:判環

該題同樣可以建立成一個圖中判環的問題。我們令i -> nums[i]爲邊建圖,那麼由於有重複數的原因,所以勢必有兩個頂點同時指向同一個頂點,此時有環出現,而環的起點就是我們要找的重複數。

可以使用快慢指針來實現。

AC代碼

上述代碼我實現了一下思路一和思路三,列在下面。

思路一:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int n_size = nums.size();
        int l = 1, r = n_size - 1;
        int mid = (l + r) >> 1;

        while (l < r) {
            mid = (l + r) >> 1;

            int x = 0;
            for (int i=0;i<n_size;i++) {
                if (nums[i] <= mid) {
                    x++;
                }
            }

            if (x <= mid) l = mid + 1;
            else r = mid;
        }

        return l;
    }
};

思路三:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow = 0, fast = 0;
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while(slow != fast);

        slow = 0;
        while (slow != fast) {
            slow = nums[slow];
            fast = nums[fast];
        }

        return slow;
    }
};

如果有問題,歡迎大家指正!!!

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