leetcode------Single Number II
标题: | Single Number II |
正确率: | 34% |
难度: | 中等 |
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?
如有疑问请联系我:e-mail:yanghg@pku.edu.cn
看这题之前还是先看下 Single Number,Single Number还是好理解的,但是到了升级版就变得很难理解了,这个也反映对了对位操作的薄弱,还是先复习下位操作,
1、“ & ” 按位与操作。应用场景:清除某些位,或者是取某些位的值,
2、“ | ” 按位与操作,应用场景:合并数据
3、“ ^ ”按位异或操作,应用场景:是特定位值取反,示例:a=tmp1,b=tmp2 交换两个值且不引入第三变量,则可以这样处理,a=tmp2^tmp1^tmp1 b=tmp1^tmp2^tmp2,看完3的介绍。 Single Number就感觉它弱爆了。
回到这个题,对于这个题我其实也是一点想法都没有!我感觉如果把这个题目解决了!那我们对于数列中有只会出现两种类型数:1、出现n个2、只有一个数出现m个。也是能解决的。看我的分析
假如说现在有一个数组A={3,5,3,3}转换成二进制A’={011,101,011,011}所有可以找到一些规律,同时出现三个数中每一位的1的个数一定是三个,单独的那个一定是一个,那么就用三个变量来存储每1的个数,也就是说,如果发现了一个位上的1到了三个那么就应该进行清零,循环比较一遍后剩下的那个统计1为一个的变量便是要得到的解。
循环遍历数组每一个位置,“位操作的相加”,迭代操作。
设定变量:a,b,c=0,0,0;
a便是统计每一位1的个数,b便是统计出现两个1,c便是统计出现三个,以下为每一次迭代的步骤;
1、b|=(a&A[i]);
2、a^=A[i];
3、c=~(a&b);
4、a&=c;
5、b&=c;
解释:
1、先处理出现两个1的问题,用a与A[i]进行与操作,用原来是1个1的情况判断该位是否还有1,如果出现A[i]对应的位也是1,则说明该位是个两个1,该位为0,则是一个1,不去处理,最后的结果与b进行或操作,将该位为两个1的情况进行统计。(b的二进制表示:1,则表示该位有两个1,0:则表示该位没有两个1)
2、用a统计出现该位一个1的数量,(1代表有一个1,0代表没有1,如:a的第一位本身就是1,A[i]的第一位也是一,则不去统计,因为这是b要考虑的事情,两个1的问题),a,A[i]对应位同时为1,则表示有两个1,要进行进位,任何一个为1则要进行置1操作,其他情况为0,刚好符合 异或操作,
3、统计1出现三次的情况,如果该位已经是现在该位对应的a是1,b是1,则说明该位已经有3个1了,那么A[i]的该位也是1,则需要进行a、b的清零操作,分别用a b与c的取反进行与操作。
1、用b去统计
如果看不懂,我来以上述那个A数组为例子进行解释
A’={011,101,011,011},a=0,b=0c=0
1、A[0]=011、b=000,a=011,c=000
2、A[1]=101、b=001,a=110,c=000
3、A[2]=011、b=011,a=101 出现了ab有相同位为1的情况,c=110
4、A[2]=011,b=010,a=100,c=110
5、A[3]=011 b=010 a=111出现了ab有相同位为1的情况,c=101
6、A[4]=011 b=000 a=101 c=101
最后发现剩下的a刚好是A'中的单独出现过的那个数字。
具体看代码:
1 public class Solution { 2 public int singleNumber(int[] A) { 3 int a=0,b=0,c=0; 4 for(int i=0;i<A.length;i++){ 5 b |= (a & A[i]); 6 a ^=A[i]; 7 c=~(a&b); 8 a&=c; 9 b&=c; 10 } 11 return a; 12 } 13 }