剑指Offer | 数组中只出现一次的数字

r囧r小猫 2022-05-23 07:35 261阅读 0赞

做了个剑指Offer的题目目录,链接如下:
https://blog.csdn.net/mengmengdastyle/article/details/80317246

一、题目

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
测试用例:
[2,4,3,6,3,2,5,5]
对应输出应该为:
“4,6”
暴力破解,就不解释了。讲一下用位运算的方法

二、与、或、异或运算

1.与运算(&)
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:两位同时为“1”,结果才为“1”,否则为0

例如:3&5 即 0000 0011 & 0000 0101 = 0000 0001 因此,3&5的值得1。
例如:9&5 即 0000 1001 (9的二进制补码)&00000101 (5的二进制补码) =00000001 (1的二进制补码)可见9&5=1。

2.或运算(|)
参加运算的两个对象,按二进制位进行“或”运算。
运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1;
  即 :参加运算的两个对象只要有一个为1,其值为1。
例如:3|5 即 0000 0011 | 0000 0101 = 0000 0111 因此,3|5的值得7。 

例如:9|5可写算式如下: 00001001|00000101 =00001101 (十进制为13)可见9|5=13

3.异或运算(^)

参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;

  即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。
例如:9^5可写成算式如下: 00001001^00000101=00001100 (十进制为12)可见9^5=12

三、讲解

如输入数组为:{2,4,3,6,3,2,5,5}

  1. 除了有两个数字只出现了一次,其他数字都出现了两次。异或运算中,任何一个数字和自己本身异或都是0,任何一个数字和0异或都是本身。
    所以:将数组中的数都异或运算后,相同的数字将被消除,得到得是只出现一次的两个数字的异或运算。

    0^2^4^3^6^3^2^5^5 = 4^6 = 0100 ^ 0110 = 0010 = 2

2.记 flag=0010;
从右向左找,找到1。

  1. 只出现一次的两个数字4(0100)和6(0110),从右向左第一个不同的数字为右边第二位;
  2. 根据这个条件可以将整个数组分成两部分,

    flag & array[i]

看运算及分组图示:
这里写图片描述

5.将两组数分别再分别 异或 运算,相同的数异或后为0;
即可得到两个数组中只出现一次的数字,4和 6;

四、代码

  1. public class Solution {
  2. public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
  3. //如array={2,4,3,6,3,2,5,5}
  4. if(array.length < 2) return ;
  5. int myxor = 0; //用0,因为0与任何数 异或后 都为 任何数
  6. //将数组中的数都异或运算后,相同的数字将被消除,得到得是只出现一次的两个数字的异或运算。
  7. for(int i = 0 ; i < array.length; ++ i ){
  8. myxor ^= array[i]; //最后得到的是 4和6的异或
  9. }
  10. //4^6 = 0100 ^ 0110 = 0010
  11. int flag = 1; //用来从右到左找哪个位首先不同
  12. while((myxor & flag) == 0){
  13. flag <<= 1; //左移一位,直到找到为 flag=0010;
  14. }
  15. for(int i = 0; i < array.length; ++ i ){
  16. if((flag & array[i]) == 0){ //以flag分组
  17. num2[0]^= array[i];
  18. }else{
  19. num1[0]^= array[i];
  20. }
  21. }
  22. }
  23. }

发表评论

表情:
评论列表 (有 0 条评论,261人围观)

还没有评论,来说两句吧...

相关阅读