演算法學習(十三)一堆資料中找到丟失數字問題
題目描述:
每個機器都有一個標號Id,每個id資料儲存兩個備份,如果一臺機器宕機,就會丟失一個備份,如果得到一個數據檔案Id的列表,是否能夠快速找到這個僅出現一次的Id?
分析:
其實就是海量資料中有一個數據是隻出現一次的,其他都是出現兩次,找到那個出現一次的資料。
解法一:
這種記錄出現次數的問題,最先想到的當然就是伴隨陣列,遍歷列表,陣列元素為資料出現次數,陣列下標可以是標號id,那個次數為1的Id就是我們要找的結果,時間複雜度為O(N),空間複雜度為O(N)。但是面對海量資料,這個空間複雜度也是夠嗆。
解法二:
能不能節省資料儲存空間呢?這個題的特點就是我們要找的是隻出現一次的,如果已經出現了兩次,那肯定不是我們要找的,於是,如果次數為2,我們就可以直接丟棄了。使用雜湊表,遇到一個元素加入到表中,次數加一,如果次數為2,就刪除對應的Id,最後剩下的id就是我們要找的,空間複雜度最好的情況下為O(1),不過最壞的也是O(N)
解法三:
我們發現相同資料出現的是偶數次,最後只剩下一個奇數次的資料,可以考慮使用位運算中的異或(^)。注意異或運算滿足交換律和結合律,其他出現兩次的Id異或完都是0。我們可以遍歷列表,然後依次加入異或運算,最後剩下的就是出現一次的id。
時間複雜度為O(N),空間複雜度為O(1)。
如果是兩臺機器宕機呢?我們能否找到故障機器?
分析:
就是有兩個Id只出現了一次,設為A和B。如果使用異或方案,最後剩下的就是A^B,如果A和B相同,就是一臺機器的兩個備份都丟失了,那麼最後A^B為0。如果A和B不相同,則A ^B 不為0,我們可以找到A^B中某一位為1的位,則A和B中只用其中一個對應位上為1,我們可以以此位為判斷依據。將Id分為兩類,一類此位為1,一類為0,然後分別使用異或的方法,每一類中可以得到一個結果,分別為A和B。
再討論A和B相同的問題,我們可以儲存所有的id的求和,然後再計算剩下的id之和,然後將原來的總和減去當前的id和,得到的是宕機機器id的和 A+ B,如果相等,直接(A+B)/2,得到宕機的Id 號。