Single Number III

https://leetcode.com/problems/single-number-iii/

Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.

继续位操作

class Solution {
public:
    vector<int> singleNumber(vector<int> &nums) {
        int res = 0;
        for(auto item: nums) res ^= item;
        int lastBit = (res & (res - 1)) ^ res;
        int i1 = 0, i2 = 0;
        for(auto item: nums) {
            if(item & lastBit) i1 ^= item;
            else i2 ^= item;
        }
        return vector<int> {i1, i2};
    }
};

注意一点,(res & (res - 1)) 作用是删除这个数的最后一个1 bit。

详细解释:引自剑指offer

在分析这种算法之前,我们先來分析把一个数减去1的情况。如果一个整数不等于0,那么该整数的二进制表示中至少有一位是1。先假设这个数的最右边一位是1,那么减去1时,最后一位变成0而其他所有位都保持不变。也就是最后一位相当于做了取反操作,由1变成了0。
接下来假设最后一位不是1而是0的情况。如果该整数的二进制表示中最右边1位于第m位,那么减去1时,第m位由1变成0,而第m位之后的所有0都变成1,整数中第m位之前的所有位都保持不变。举个例子:一个二进制数1100,它的第二位是从最右边数起的一个1。减去1后,第二位变成0,它后面的两位0变成1,而前面的1保持不变,因此得到的结果是1011。
在前面两种情况中,我们发现把一个粮数减去1,都是把最右边的1变成0。如果它的右边还有0的话,所有的0都变成1,而它左边所有位都保持不变。接下来我们把一个整数和它减去1的结果做位与运算,相当于把它最右边的1变成0。还是以前面的1100为例,它减去1的结果是1011。我们再把1100和1011做位与运算,得到的结果是1000。我们把1100最右边的1变成了0,结果刚好就是1000。我们把上面的分析总结起来就是:把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0。

int NumberOf1(int n)
{
    int count = 0;
    while(n)
    {
        ++count;
        n=(n-1)&n;
    }
    return count;
}
原文地址:https://www.cnblogs.com/daijkstra/p/4810410.html