LeetCode 剑指offerQ56 -l 数组中数字出现的次数

思路

首先,我们现了解^异或这个位运算符,它有以下的性质:

  1. 一个数和自己异或结果为0
  2. 任何数和0异或结果都是本身

不懂的百度

因为这个数组是包含两个不同的数假设为a和b,还有若干个出现两次的数。所以我们通过^运算,可以将若干个出现两次的数都排除只留下a和b的异或值s。

for(int i:nums)
s^=i;//这个s最后就是a^b的结果

现在我们拿到了a和b的异或值,下一步就是如何将a和b分解出来。我们知道因为a!=b,所以s的二进制肯定有一位为1,且这一位能够区别出a和b,换句话说就是若a的该位置为1,那么b的该位置就为0。

int a=3,b=5,s=6;//假设现在这两个不同的数,他们的异或为6
3 00000101
5 00000011
^ 
6 00000110
//那么首先我们先找出能够区别a和b的那一位1
int k=s&(-s) //这个操作能够获取s的为1的最低位的那个数
 s  6 00000110
-s -6 10000110 原码
-s -6 11111001 反码
-s -6 11111010 补码 //这里三步求的是-s的二进制表示,负数的二进制就是它的补码
然后将s&(-s)
 s 00000110
-s 11111010 
 k 00000010 这个k就是s各个位中最小的1位。
 这样我们拿到这个k之后就可以分辨出a和b了。
 我们将k和每一个num进行&操作进行分组,若为0,则表示这个num的那一位为1这一组,否则则表示这个num的那一位为0的那一组,因为a和b的那一位是不同的,则a和b肯定会被分到不同的组中。然后我们再每组中进行^操作去除出现两次的值,就分别得到a和b了。
 

int k=s&(-s);
int re[]=new int[2]; //假设a的那一位为1,b的为0
for(int i:nums){
   if((k&i)==0){//这一组表示那一位为1的数,包含a
   re[0]^=i;//然后这一组肯定是有若干出现两次的数和a组成,可以通过^取出a
   }
   else{//这一组表示那一位为0的数,包含b
    re[1]^=i;
    }
}

代码

class Solution {
    public int[] singleNumbers(int[] nums) {
      int s=0;
      for(int i:nums)
      s^=i;//最后这个s就是那两个不同的数a和b的异或值
      int k=s&(-s);//这个k就是a或b中最低的1的值
      int[] re=new int[2];
      for(int i:nums){
          if((i&k)!=0) re[0]^=i;
          else re[1]^=i;
      }
      return re;

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值