LeetCode - Find the Duplicate Number

題目

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Note:

  • You must not modify the array (assume the array is read only).
  • You must use only constant, O(1) extra space.
  • Your runtime complexity should be less than O(n2).
  • There is only one duplicate number in the array, but it could be repeated more than once.

分析

這個題是兩部分問題,第一部分的證明看起來比較直接,在數學上可以通過反證法證明。

第二部分是說現在有 n+1 個位置,放進去的數字都是從 1 到 n 的,但是存在一個重複的數字,其他的都不重複,讓用 O(1) 的空間複雜度和少於O(n2)的時間複雜度找出這個重複的數字。示例輸入輸出可能是

[2,3,1,2] -> 2
[2,2,1,2,2] -> 2

一個比較詳細的解答可以參考
https://segmentfault.com/a/1190000003817671

這裏我想解釋一下鏈接中的最後一個方法:映射找環法。代碼可以參考上述鏈接,這裏只是將其原理進行一個詳細的論述

可以先看一下上述鏈接中的解釋,這個算法的思路如下:

將數組中每個元素都看成鏈表中的一個節點,鏈表節點有兩個基本要素就是節點的值(val)和指向下一個節點的指針(next),我們假設這個數組表示成 array[], 某個節點的索引是 index,在這個算法所想象的鏈表中:

  • 節點的值 val 就是數組在這個位置的值 array[index]
  • 節點的指向想一個節點的指針 next 也是數組在這個位置的值 array[index]

舉個例子,數組 [2,3,1,2] 構成的鏈表如下圖所示

這裏寫圖片描述

我們發現這個圖中出現了一個環,下面我們說明在給定的條件下,從數組的第0個元素出發,一定會進入一個環中,且這個環包含了重複的那個數字:

  1. 假設從第0個元素出發,最後沒進入環中(反證),這就說明由0出發的鏈表的尾節點的next是null
  2. 然而給定條件下,數組中每個元素的值都是1 - n之間的數字(同時根據之前的構造方法數組的值也就是其next指針),說明這個鏈表中不會出現指向null的節點

上面的1和2相互矛盾,說明從數組的第0個元素出發,一定會進入一個環中

我們知道,指向環的入口元素(上例中的array[2]=1)的指針值(next)相同,而根據構next又等於節點的val,說明換入口元素的上一個節點就是重複的那個值。

事實上構造出來的圖中可能存在多個環,但由於題的條件限定了只可能有一個重複元素,所以其中只有一個環會帶着一個或多個“尾巴”,這個“尾巴”是進入環之前的部分。這個題的巧妙之處就在於數組中元素都在1-n之間,說明不會有節點的next值等於0,即array[0]一定是這樣的一個“尾巴”的頭部,而不會出現在環的內部。

這樣原問題就轉化爲了在單鏈表中找環入口的問題(重複的數字是環入口前一個節點),利用快慢指針的辦法可以很容易的寫出代碼(網上有很多資料,也可以看上文鏈接中的代碼)

總結一下上面的幾個關鍵點

  • 將數組看成一個鏈表,鏈表的 val 和 next 都是數組元素值
  • 從第0個元素出發,必然會形成環
  • 重複的數字節點在環的入口的前一個節點處

最後我想說的是,用數組表示鏈表或樹狀也是一種常見的辦法,效率很高,以前學數據結構的時候學到過但後來一直沒用過,可以看看 java 的 TImer 類的源代碼中最小堆是如何實現的。

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