這是一個有趣的面試題。
很容易想到的方法是:我們可以用減法,1 + 2 + ... + 100的和減去袋子中所有數的和,其差就是丟失的那個數字。1 + 2 + ... + 100
可以由等差數列公式100 * (1 + 100) / 2 = 5050算出,我們只需要遍歷一次袋子中的數字即可。
把這道題推廣到一般情況:設 π 是{1, 2, ... , n}的一個全排列,π-1 是 π 中丟失了一個數字后的序列。找出序列 π-1 中缺失的數字。
當 n 比較小時,我們仍可以用上面的方法,但是當 n 很大時,上面的方法就會有溢出的問題。雖然我們可以合理安排加減法的順序以避
免溢出,但實際上我們有更巧妙的方法來解決這類問題。
回憶異或(XOR)運算的性質:
我們從1異或到100,然后再和袋子里的數逐個異或,因為一個數與自身異或,結果是0,所以異或最后的結果就是丟失的數字。
Python 代碼:
""" returns the missing number of sequence, which is supposed to be
a permutation of {1,..,n} with one number missing.
"""
x = n
for i in xrange(1, n):
x ^= i
for e in sequence:
x ^= e
return x
顯然,用上述方法不能同時求出兩個數。這是否意味着這種巧妙的方法不再適用了?讓我們先看一下,用上述方法求出的是什么。
設丟失的兩個數字為a和b,那么上述方法求出的結果就是 a XOR b。我們能否利用這個結果呢?答案是YES。因為a != b,那么
a 和 b 的二進制表示形式中一定至少有一位相異。我們可以根據該位把數字分成兩組,該位為0的一組和該位為1的一組。兩組分別
用上述方法異或,最后兩組異或的結果就是 a 和 b。
Python 代碼:
""" returns the missing two numbers of sequence, which is supposed
to be a permutation of {1,..,n} with two numbers missing.
"""
x = n
for i in xrange(1, n):
x ^= i
for e in sequence:
x ^= e
diff = x & (-x) # isolates the rightmost 1-bit
a, b = 0, 0
for i in xrange(1, n + 1):
if i & diff:
a ^= i
else:
b ^= i
for e in sequence:
if e & diff:
a ^= e
else:
b ^= e
return a, b
注意在上面的方法中,我們要掃描兩遍這個序列。雖然復雜度仍是O(n),但是在某些特殊情況下,我們可能沒有第二次掃描的機會。
那么,能否只掃描一遍就算出結果呢?請看尋找丟失的數字(二)。