数组中每个数会出现3次,只有1个例外的数仅出现1次。 Single Number

142 篇文章 20 订阅
51 篇文章 0 订阅

题目源自于leetcode。好题。用位运算来模拟多进制模加法。

题目:Given an array of integers, every element appears three times except for one. Find that single one.
Note:Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

题目提示要用O(N)时间, O(1)空间。

思路:这道题要比http://blog.csdn.net/ojshilu/article/details/9336235 难一些。同一系列的还有http://blog.csdn.net/ojshilu/article/details/9336577,都应该往位运算上去想。

方法一:

    int类型有32位,假如二进制位不进位的话,对数组中除了single数以外的所有数做累加,32位每一位上的值都是3。假如我对每一位是做模3加法的话,把数组所有数加起来,32位每一位的值应该有1有0,因为三个相同数加起来都会模3变为0,所以剩下的1都是属于single数的,所以这个累加和就等于single数。

    这个方法时间是O(32*N),但要分别计算每一位的累加,分别作32次累加。空间是O(1)。

class Solution {
public:
    int singleNumber(int A[], int n) {

        int result = 0;
        int sum;
        int i, j;
        for(i=0;i<32;i++)
        {
            sum = 0;
            for(j=0;j<n;j++)
                sum += ((A[j]>>i)&1); //每轮只对固定某一位做累加
            
            if(sum%3 == 1)
                result = result|(1<<i);
        }
        return result;
    }
};

方法二:

    方法一之所以要单独计算每一位的累加,是因为二进制位无法实现能累加到3并且还不进位。想要不进位(不进位的话才能并行着操作),我们就使用与或非位运算;想要能累加到3,我们需要自定义一些规则来实现它。

    我想用与/或/非/异或来模拟实现位的模3加法。这样的加法有三个状态0、1、2,但是二进制位只有0、1两个状态,所以我需要借助一个数来作辅助。int型变量ones它的每一位的意义是,如果该位为1,则表示该位的当前累加和为1。int型变量twos它的每一位的意义是,如果该位为1,则表示该位的当前累加和为2。那么如果某一位在ones和twos都为0,说明该位的当前累加和为0。(下面制定的加法规则会让ones和twos中的某位不可能都是1)


那么加法的过程有这几种情况,对于某一位,ones和twos中的对应位如何变化:

1、加数X为0:ones不变,twos不变。

2、加数X为1:若之前ones为1,则ones变0,twos变1。

                        若之前twos为1,则ones还是0,twos变0

                        若之前都为0,则ones变1,twos还是0。


具体的情况如下表格:

onetwoX newonenewtwo 加法
000 00 0+0=0
010 01 2+0=2
100 10 1+0=1
001 10 0+1=1
011 00 2+1=0
101 01 1+1=2

观察上面的表格,这个过程很类似数字电子课程里面的组合逻辑电路设计。


根据规律,构造出一种运算(肯定不只有这1种):

    ones = (~twos) & (ones ^ X); (一定注意:执行完这句之后,ones值就变成新的了!)

    twos = (~ones) & (twos ^ X);

对题目数组中所有数累加之后,必定是twos全部为0,ones有1有0,此时的ones就是我们要找的single数。


    方法二的时间复杂度更好,只累加一次即可。这种用位运算模拟加法的方法可以达到多个位同时相加的并行的效果。当然它的使用限制是,累加最终结果的情况是要提前知道的,这里就是知道累加最终一定是ones有1有0,twos全0,累加结果就体现在这里。

代码:

class Solution {
public:
    int singleNumber(int A[], int n) {
        int ones = 0;
        int twos = 0;
        int i;
        for(i=0;i<n;i++)
        {
            ones = (~twos) & (ones ^ A[i]);
            twos = (~ones) & (twos ^ A[i]);//注意这里用的ones是新的ones,这一点和数电不同。
        }
        return ones;
    }
};

方法二的思想源于http://www.cnblogs.com/daijinqiao/p/3352893.html


  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值