最近碰到很多通過巧妙着運用位運算來巧妙解決復雜問題的算法,今天分享的這道題,或許能夠開拓你的一些算法思維。
該問題是這樣的:
有一組存放 ID 的數據。並且 ID 取值為 0 - (N-1) 之間,其中只有一個 ID 出現的次數為 1,其他的 ID 出現的次數都等於 2,問如何找到這個次數為 1 的 ID ?
解法一:巧用數組下標
不知道有多少人還記得我之前分享的巧用數組下標的技巧:一些常用的算法技巧總結。
我的第一想法便是采用下標法來解決,把 ID 作為數組 arr 的下標,在遍歷 ID 的過程中,用數組記下每個 ID 出現的次數,即每次遍歷到 ID = n,則 arr[n]++。
之后我們在遍歷數組 arr,找到 arr[n] = 1 的ID,該下標 n 便是我們要尋找的目的 ID。
這種方法的時間復雜度為 O(N),空間復雜度為 O(N)。
解法二:巧用哈希表
顯然時間復雜度是無法再降低的了,因為我們必須要遍歷所有的 ID,所以時間復雜度最少都得為 O(N)了,所以我們要想辦法降低空間復雜度。
大家想一個問題,假如我們檢測到某個 ID 已經出現了 2 次了,那么這個 ID 的數據我們還需要存儲記錄嗎?大部分的 ID 都出現了 2 次,這一大部分的數據真的需要存儲嗎?
答是不用的,因為出現 2 次的 ID 不是我們所要找的。所以我們可以優化解法一,我們可以采用哈希表來記錄 ID 出現的次數:利用哈希表記下每個 ID 出現的次數,每次遇見一個 ID,就把這個 ID 放進 哈希表,如果這個 ID 出現了次數已經為 2 了,我們就把這個 ID 從哈希表中移除,最后哈希表只會剩下一個我們要尋找的 ID。
這個方法最好的情況下空間復雜度可以降低到 O(1),最壞的情況仍然了 O(N)。
解法三:巧用位運算
那究竟有沒辦法讓空間復雜度在最壞的情況下也是 O(1) 呢?
答是有的,按就是采用異或運算。異或運算有個特點:
異或運算特點:相同的兩個數異或之后,結果為 0,任何數與0異或運算,其結果不變並且,異或運算支持結合律。
所以,我們可以把所有的 ID 進行異或運算,由於那些出現兩次的 ID 通過異或運算之后,結果都為 0,而出現一次的 ID 與 0 異或之后不變,又因為異或支持結合律,所以,把所有 ID 進行異或之后,最后的結果便是我們要找的 ID。
這個方法的空間復雜度為 O(1),巧妙利用了位運算,而且運算的效率是非常高效的。
問題拓展
假如有 2 個 ID 出現的次數為 1,其他 ID 出現的次數都為 2 呢?有該如何解決呢?是否還是可以用位運算呢?
為了方便這里我們先假設 異或 的符號為 @,
答是必須的,假如這兩個出現一次的 ID 分別為 A, B,則所有 ID 異或后的結果為 A@B,這時我們遇到的問題是無法確定 A,B的值。
由於 A 和 B 是不一樣的值,所以 A@B 的結果不為 0,也就是說,這個異或值的二進制中某一位為1。顯然,A 和 B 中有且僅有一個數的相同位上也為 1。
這個時候,我們可以把所有 ID 分為兩類,一類在這個位上為 1,另一類為 0,那么對於這兩類,一類會含有 A,另一類會含有 B。於是,我們可以分別計算這兩類 ID 的異或值,幾可得到 A 和 B 的值。
總結
大家做刷題的時候,不妨多加上一個想法:是否可以用的上位運算這種思路。有收獲?不妨來個好看讓更多人人看到這篇文章!
最后推廣下我的公眾號:苦逼的碼農:戳我即可關注,文章都會首發於我的公眾號,期待各路英雄的關注交流。