1. 程式人生 > >陣列中三個只出現一次的數字

陣列中三個只出現一次的數字

題目:一個數組中有三個數字abc只出現一次,其他數字都出現了兩次。請找出三個只出現一次的數字。

如果我們把陣列中所有數字都異或起來,那最終的結果(記為x)就是abc三個數字的異或結果(x=a^b^c)。其他出現了兩次的數字在異或運算中相互抵消了。

我們可以證明異或的結果x不可能是abc三個互不相同的數字中的任何一個。我們用反證法證明。假設x等於abc中的某一個。比如x等於a,也就是a=a^b^c。因此b^c等於0,即b等於c。這與abc是三個互不相同的三個數相矛盾。

由於xabc都各不相同,因此x^ax^bx^c都不等於0

我們定義一個函式f(n),它的結果是保留數字

n的二進位制表示中的最後一位1,而把其他所有位都變成0。比如十進位制6表示成二進位制是0110,因此f(6)的結果為2(二進位制為0010)。f(x^a)f(x^b)f(x^c)的結果均不等於0

接著我們考慮f(x^a)^f(x^b)^f(x^c)的結果。由於對於非0nf(n)的結果的二進位制表示中只有一個數位是1,因此f(x^a)^f(x^b)^f(x^c)的結果肯定不為0。這是因為對於任意三個非零的數ijkf(i)^f(j)的結果要麼為0,要麼結果的二進位制結果中有兩個1。不管是那種情況,f(i)^f(j)都不可能等於f(k),因為f(k)不等於0,並且結果的二進位制中只有一位是1

於是f(x^a)^f(x^b)^f(x^c)的結果的二進位制中至少有一位是

1。假設最後一位是1的位是第m位。那麼x^ax^bx^c的結果中,有一個或者三個數字的第m位是1

接下來我們證明x^ax^bx^c的三個結果第m位不可能都是1。還是用反證法證明。如果x^ax^bx^c的第m位都是1,那麼abc三個數字的第m位和x的第m位都相反,因此abc三個數字的第m位相同。如果abc三個數字的第m位都是0x=a^b^c結果的第m位是0。由於xa兩個數字的第m位都是0x^a結果的第m位應該是0。同理可以證明x^bx^cm位都是0。這與我們的假設矛盾。如果abc三個數字的第m位都是1x=a^b^c結果的第m位是1。由於xa兩個數字的第m位都是

1x^a結果的第m位應該是0。同理可以證明x^bx^cm位都是0。這還是與我們的假設矛盾。

因此x^ax^bx^c三個數字中,只有一個數字的第m位是1。於是我們找到了能夠區分abc三個數字的標準。這三個數字中,只有一個數字滿足這個標準,而另外兩個數字不滿足。一旦這個滿足標準數字找出來之後,另外兩個數字也就可以找出來了。

這種思路的C++程式碼如下:

void getThreeUnique(vector<int>& numbers, vector<int>& unique)
{
    if(numbers.size() < 3)
        return;
   
    int xorResult = 0;
    vector<int>::iterator iter = numbers.begin();
    for(; iter != numbers.end(); ++iter)
        xorResult ^= *iter;
 
    int flags = 0;
    for(iter = numbers.begin(); iter != numbers.end(); ++iter)
        flags ^= lastBitOf1(xorResult ^ *iter);
    flags = lastBitOf1(flags);
   
    // get the first unique number
    int first = 0;
    for(iter = numbers.begin(); iter != numbers.end(); ++iter)
    {
        if(lastBitOf1(*iter ^ xorResult) == flags)
            first ^= *iter;
    }
    unique.push_back(first);
   
    // move the first unique number to the end of array
    for(iter = numbers.begin(); iter != numbers.end(); ++iter)
    {
        if(*iter == first)
        {
            swap(*iter, *(numbers.end() - 1));
            break;
        }
    }
   
    // get the second and third unique numbers
    getTwoUnique(numbers.begin(), numbers.end() - 1, unique);
}
 
int lastBitOf1(int number)
{
    return number & ~(number - 1);
}
 
void getTwoUnique(vector<int>::iterator begin, vector<int>::iterator end, vector<int>& unique)
{
    int xorResult = 0;
    for(vector<int>::iterator iter = begin; iter != end; ++iter)
        xorResult ^= *iter;
   
    int diff = lastBitOf1(xorResult);
   
    int first = 0;
    int second = 0;
   
    for(vector<int>::iterator iter = begin; iter != end; ++iter)
    {
        if(diff & *iter)
            first ^= *iter;
        else
            second ^= *iter;
    }
   
    unique.push_back(first);
    unique.push_back(second);
}