剑指offer—数组中只出现一次的数字

题目

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

题目解释,比如有数组nums={1,2,4,3,2,1,6,6},找出两个只出现了一次的数,则输出结果为 3,4。

退一步讲,如果一个数组中,只有一个数出现一次,其余的数都出现两次,寻找该数。该题如何做呢?

可以利用位运算中的异或。
位运算性质如下:

符号描述结果
&与运算对应的二进制为都为1,结果才为1
|或运算只有两个数二进制位有一个为1,结果则为1
^异或运算两个数对应的二进制为不同则为1,相同则为0
~取反运算二进制位1,结果为0;二进制位0,结果为1

利用位运算可以得到如下两个结果:

  1. 任意一个数异或本身,得到的结果为0。
  2. 0异或任意一个数,得到的结果为该数。

如果我们可以将数组划分为两个子数组,每个子数组里面包含一个只出现一次的数字,然后针对每个子数组内部一次做异或运算,因为相同的数异或之后都变成0,最后每个数组都会留下一个数字,该数即为只出现一次的数。

现在的问题关键在于如何划分数组了?
还是将原数组依次异或,出现两次的数异或之后为0,之后整个数组将会留下两个只出现一次的数,由于这两个数不相同,异或的结果肯定不为0。
只需要找到最后异或的结果中最低位为1的数,并记录下最低位为1的下标,记录下标为index,现在以index位是否等于1,将整个数组划分为两个子数组,两个数组特点为:

  1. 第一个数组index位置的二位数等于1。
  2. 第二个数组index位置的二位数等于0。
    因此如果两个数相同,则两个数一定在同一个子数组中,因为相同的数的第index位置一定是相同的,不可能将两个相同的数分配到两个不同的子数组中,并且每个子数组中都只包含一个出现一次的数。

然后只需要分别异或两个子数组,即可得到每个子数组中出现一次的数。




代码如下

public class Solution {
    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
        int length = array.length;
        if(length == 2){
            num1[0] = array[0];
            num2[0] = array[1];
        }
        int bitResult = 0;
        for(int i = 0;i < length; i++){
            bitResult ^= array[i];
        }
        // 计算数中的最低位的1的下标
        int index = getLowOne(bitResult);
        // 将原数据进行分组
        for(int i = 0; i < length; i++){
            if(numIsOne(array[i],index)){
                num1[0] ^= array[i];
            }else{
                num2[0] ^= array[i];
            }
        }
    }
    
    // 计算数组中最低位为1的下标
    public int getLowOne(int num){
        int index = 0;
        while(((num & 1)==0 )&& index < 32){
            num = num >> 1;
            index ++;
        }
        return index;
    }
    
    // 判断某个数对应的下标是否是1
    public boolean numIsOne(int num,int index){
        if(((num>>index) & 1) == 1){
            return true;
        }
        return false;
    }
}

2. 总结

该题主要考察数的位运算,可以尝试由简变难的处理方式,先尝试处理数组中只有一个出现一次的数,再处理数组中有两个出现一次的数。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值