leetcode : Single Number

这个题一个有三道:

Single Number i:

给n个整数,其中有一个数只出现了一次,其他的数都出现了两次,求那个single number,这个很简单,把所有的数字做一下异或,得到的结果就是那个single dog。

 

Single Number ii:

给n个整数,其中所有的整数都出现了三次,除了一个single number。

这里有两个思路,第一种是对每一个二进制位统计出现次数,因为除了single number以外的数都出现了三次,我们可以统计每个二进制位中1出现的次数k,而k%3就是single number在这一位贡献的1的个数。

AC代码:

class Solution {
public:
     int singleNumber(int A[], int n) {  
       int bitCount = sizeof(int) * 8,result = 0;
        int *arr = new int[bitCount];
        for(int i = 0; i < bitCount; ++i){
            arr[i] = 0;
        }

        for(int i = 0; i < n; ++i){
            for(int j = 0; j < bitCount; ++j){
                arr[j] += (A[i] >> j) & 1;
            }
        }

        for(int i = 0; i < bitCount ; ++i){
            result |= (arr[i]%3) << i;
        }
        delete arr;
        return result;
    }  
};

 

但是这么写比较慢,因为对每个n都要做32次位移分别统计每一位是否为1,可以尝试改进一下,毕竟位操作直接用个数组存每一位出现的次数有点low。

所以尝试用一个整数保存每一位出现的次数。想办法把出现三次变成出现四次,再异或到一起就可以了。

可以用三个数来代替这个数组:

对于one中每个为1的位,表示到当前为止,这些位上出现了k*3 + 1个1,

对于two中每个为1的位,表示到目前为止,这些位上出现可k*3 + 2个1,three同理

AC代码:

class Solution {
public:
    int singleNumber(int A[], int n) {
        int one=0, two=0, three=0;
        for(int i=0; i<n; i++){
            two |= one&A[i];
            one^=A[i];
            //cout<<one<<endl;
            three=one&two;
            one&= ~three;
            two&= ~three;
        }
        return one;
    }
};

解释一下这个代码:

two |= one&A[i]

这里 |=是为了置1,即把那些同时在one 和 A[i]中的1放进two中(已经出现了3*k + 1次,又出现了一次,于是变成了3*k + 2次,放在two中记录)

one ^= A[i],这里比较清楚,一开始出现了0次,后来又出现了一次才放进one,否则将这一位清零(共计出现两次,所以不在one中)

three = one&two ,这里是有点容易迷惑的,某一位第3*k次出现时,首先会将one置1,然而它并不会影响two,所以three就是那些已经出现了3K+2数,又出现了一次时,记录下来。

这也意味着将one和two中的1 “吸收”进了three,所以要把one 和two中这些位清0,所以ont &= ~three, two &= ~three.

这个方法很扯淡,但是很快且省空间- -!

 

Single Number iii

n个整数中,每个数都出现了两次,除了某两个数都只出现了一次,这道题比起single number i 要难一些,在一堆情侣中找一只单身狗自然要比找一对基佬容易些的,思路的关键在与将问题转换为single number i

就是想办法把这两个single number分开,假设他们分别为A,B,那么对所有的数异或就得到A ^ B,所以这个A^B必然是不等于0的,因为A B都只出现了一次,相等就变成出现两次了,那么随便从A ^ B中找一个是1的位,不妨设为第k位,那么可以将这个n个数分成两拨,一波是第k位为1的,另一波是第k位为0的,那么问题就变成Single Number i了。

代码中使用mask = AorB & (~(AorB-1))取得了一个屏蔽字,只保留最左的1,其他位置0.

AC代码:

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unsigned int AorB = 0;
        for (int elem : nums) {
            AorB ^= elem;
        }
        int mask = AxorB & (~(AxorB-1));;
        int A = 0, B = 0;
        for (int elem : nums) {
            if (elem & mask) {
                A ^= elem;
            } else {
                B ^= elem;
            }
        }
        return vector<int>{A, B};
    }
};

 

posted on 2015-09-30 22:19  远近闻名的学渣  阅读(146)  评论(0)    收藏  举报

导航