LeetCode 第 287 號問題:尋找重複數,一道很是簡單的數組遍歷題,加上四個條件後感受無從下手

今天分享的題目來源於 LeetCode 第 287 號問題:尋找重複數。算法

題目描述

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。編程

示例 1:數組

輸入: [1,3,4,2,2]
輸出: 2複製代碼

示例 2:spa

輸入: [3,1,3,4,2]
輸出: 3複製代碼

說明:指針

  • 不能更改原數組(假設數組是隻讀的)。
  • 只能使用額外的 O(1) 的空間。
  • 時間複雜度小於 O(n2) 。
  • 數組中只有一個重複的數字,但它可能不止重複出現一次。

題目解析

我的博客:www.cxyxiaowu.comcode

給定一個整形數組,數組的長度是 n + 1,數組裏面存放的元素是區間 [1, n] 上的數,這個數組中有且僅有一個元素是重複的,題目讓咱們找出這個元素,而且這道題給了以下的限制:排序

  • 不能改變數組
  • 只能使用 O(1) 的空間
  • 時間複雜度必須小於 O(n^2)
  • 重複的元素可重複屢次

首先不能改變數組致使沒法排序,也沒法用 index 和元素創建關係;博客

只能使用 O(1) 的空間意味着使用哈希表去計數這條路也走不通;io

時間複雜度必須小於 O(n^2) 表示暴力求解也不行;ast

重複的元素可重複屢次 這一條加上後,原本能夠經過累加求和而後作差 sum(array) - sum(1,2,...,n) 的方式也變得不可行。

原本是很是簡單的一道數組遍歷的題目,加上了上面這四個條件後感受有點無從下手

咱們說作題要藉助算法,而不是空想,所以對於這道題也不例外,咱們能夠問本身這樣一個問題,就是 「什麼樣的算法能夠不使用額外的空間解決數組上面的搜索問題?」

靜靜的思索一下。

你這時應該隱隱約約知道了,難道是 二分查找

什麼?二分查找法?!

二分查找法不是對有序數組才適用麼?

這裏澄清一個誤區,二分法的使用 並不必定 須要在排序好的數組上面進行,不要讓常見的例題限制了你的思路,二分法還有一個比較高級的用法叫作 按值二分

這道題目交代的信息不多,咱們只須要關注兩個東西 - 數組,數組裏的元素,利用二分咱們須要去思考的是,咱們要找符合條件的元素做爲答案,那麼比答案小的元素具備什麼樣的特質,比答案大的元素又具備什麼樣的特質?,結合題目給咱們的例子來看看:

說明:下面的 <= 符號代表="" 小於或者等於。)

例1:

[1,3,4,2,2]                         元素個數
<= 1 的元素:1                          1
<= 2 的元素:1, 2, 2                    3
<= 3 的元素:1, 2, 2, 3                 4
<= 4 的元素:1, 2, 2, 3, 4              5複製代碼

例2:

[3,1,3,4,2]
<= 1 的元素:1                          1
<= 2 的元素:1, 2                       2
<= 3 的元素:1, 2, 3, 3                 4
<= 4 的元素:1, 2, 3, 3, 4              5複製代碼

極端一點的例子 (必須保證數組的長度是 n + 1, 而且元素都在區間[1,n] 上, 有且只有一個重複)

[3,3,3,3,4]
<= 1 的元素:                           0
<= 2 的元素:                           0
<= 3 的元素:3, 3, 3, 3                 4
<= 4 的元素:3, 3, 3, 3, 4              5複製代碼

看完上面幾個例子,相信你明白了一個事實:

  • 「若是選中的數 小於 咱們要找的答案,那麼整個數組中小於或等於該數的元素個數必然小於或等於該元素的值;
  • 若是選中的數 大於或等於 咱們要找的答案,那麼整個數組中小於或等於該數的元素個數必然 大於 該元素的值」

並且你能夠看到,咱們要找的答案其實就處於一個分界點的位置,尋找邊界值,這又是二分的一個應用,並且題目已經告訴咱們數組裏面的值只可能在 [1, n] 之間,這麼一來,思路就是在 [1, n] 區間上作二分,而後按咱們以前提到的邏輯去作分割。整個解法的時間複雜度是 **O(nlogn)**,也是知足題目要求的。

上面的解法不是最優的,可是我的以爲是根據現有的知識比較容易想到的。

另一種 O(n) 的解法借鑑快慢指針找交點的思想,算法很是的巧妙,也很是的有趣,但不太容易想到,這裏把代碼放上。

代碼實現一

//二分查找
class Solution {
    public int findDuplicate(int[] nums) {
         int len = nums.length;
        int start = 1;
        int end = len - 1;

        while (start < end) {
            int mid = start + (end - start) / 2;
            int counter = 0;
            for (int num:nums) {
                if (num <= mid) {
                    counter++;
                }
            }
            if (counter > mid) {
                end = mid;
            } else {
                start = mid + 1;
            }
        }
        return start;
    }
}複製代碼

代碼實現二

//快慢指針
public int findDuplicate(int[] nums) {        
    int fast = nums[nums[0]];
    int slow = nums[0];
    
    while (fast != slow) {
        fast = nums[nums[fast]];
        slow = nums[slow];
    }
    
    slow = 0;
    while (fast != slow) {
        fast = nums[fast];
        slow = nums[slow];
    }
    
    return slow;
}複製代碼

❤️ 看完三件事:

若是你以爲這篇內容對你挺有啓發,我想邀請你幫我三個忙:

  • 點贊,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓 -_-)
  • 關注我和專欄,讓咱們成爲長期關係
  • 關注公衆號「五分鐘學算法」,第一時間閱讀最新的算法文章,公衆號後臺回覆 1024 送你 50 本 算法編程書籍。
相關文章
相關標籤/搜索