剑指Offer-面试题56-I:组中数字出现的次数 异或位运算

这里是题目描述:剑指Offer-面试题56-I:组中数字出现的次数

相似题目:LeetCode-136.只出现一次的数字

对于这道题,我们很容易就能想到使用哈希表统计数组中每个不同数字出现次数的方法,但是它不满足题目要求的空间复杂度**O(1)**的限制。要满足常数空间开销,我们可以使用位运算中的 异或运算

异或运算介绍

在java中,异或位运算的操作符为^,如1与0异或运算,写作 1^0。异或运算则是对于两个操作数相互对应的每一位,如果两个操作数这一位相等(都为0或都为1),则运算结果的该位是1否则是0。如下表:

在这里插入图片描述
例如5^3用二进制表示为101^011,则结果的二进制为110,就是十进制的6

异或运算有一个重要的特性:任何数和0进行异或的结果都等于它自身,我们通过分析异或的基本定义可以很容易证明。同时它的逆表述:任何数和它自身进行异或的结果都等于0 也成立

接下来,我们使用这两条特性解决本题

我们使用0和给定数组nums中的所有值进行异或运算,那么得到0^nums[0]^nums[1]...^nums[n-1]^nums[n]nums中有两个只出现一次的数字m1m2,那么异或结果中其他所有出现两遍的数字都和自己发生过异或成为0,因此异或的结果为m1^m2

得到m1^m2后,我们要根据它将m1m2求出来。因为m1m2不相同,因此它们的二进制表示肯定有不同值的位,这样的位在m1^m2中值为1。因此我们从最低位开始向最高位寻找m1^m2中最低的值为1的位,寻找方法可以是和初始值为1的数字进行&运算,当&运算结果不为0,则说明该位不为0;否则数字1左移一位再进行&运算,相当于从低到高寻找不为0的位

找到m1^m2第一个不为0的位后,我们可以根据这一位是否为1nums中的数字分成两个部分,那么两个只出现一次的数字m1m2肯定不位于同一部分,而其他出现两次的数字肯定位于同一部分。再分别用0和这两部分的数字进行异或运算,就会最终得到m1m2

题解代码

class Solution {
    //使用位运算中的异或运算
    //异或运算特性:1.任何数和它自身异或得0 2.任何数和0异或得它自身
    public int[] singleNumbers(int[] nums) {
        if(nums.length==2)
        {
            return nums;
        }
        int temp=0;
        for(int i=0;i<nums.length;i++) //根据异或的性质,temp和nums所有元素异或运算,nums中唯二的出现一次元素n1、n2,temp=n1^n2
        {
            temp^=nums[i];
        }
        int the1loc=1;
        while((temp&the1loc)==0) //确定temp最低的1的位,也是n1和n2最低的不同位
        {
            the1loc=the1loc<<1; //左移一位
        }
        int n1=0,n2=0;
        for(int i=0;i<nums.length;i++)
        {
            if((nums[i]&the1loc)==0)
            {
                n1^=nums[i];
            }
            else
            {
                n2^=nums[i];
            }
        }
        return new int[] {n1,n2};
    }
}

时间复杂度:O(n)
空间复杂度:O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值