【Leetcode】只出现一次的数字(异或中的骚操作)

异或中的骚操作

今日刷LeetCode发现了只出现一次的数字的系列题,并且深深领会到异或中的骚操作,接下来介绍一些说完让你眼前一亮的操作

首先异或的基础介绍

什么是异或操作,简单来说就是相同为0,不同为1,更好记忆的方法是不进位+运算,举个简单的栗子:6^7=1

所以不难得到:0^N=N,N^N=0

异或操作是满足结合律和交换律的,即多个数即异或结果和顺序无关
在这里插入图片描述

不需要额外空间的互换值

maybe交换两个数的值是我们最开始接触过的代码,很简单,用一个额外变量

		int a = 1,b = 2,temp;
        temp = a;
        a = b;
        b = temp;

但如果使用异或操作可以不使用额外的空间

		int a = 1,b = 2;
        a = a^b;
        b = a^b;
        a = a^b;
        //输入a,b你会发现a,b交换了数值

怎么实现的呢?
在这里插入图片描述

但请注意:

对于使用两块不同内存空间的数值,这么操作没有问题,假如两个数共享的是一个内存区域,譬如:

public static void main(String[] args) {

        int a = 2,b=2;
        a = a^b;
        b = a^b;
        a = a^b;
        System.out.println(a+"  "+b);// a = 2,b=2
        int[] arr = {1,2,3};
        arr[0] = arr[0]^arr[0];
        arr[0] = arr[0]^arr[0];
        arr[0] = arr[0]^arr[0];
        System.out.println(arr[0]);//arr[0] = 0;
    }

其实不难理解,交换两个数值最起码需要两块内存空间,如果只有arr[0]一块空间,自己来回异或自己结果肯定为0

虽然这操作不常见,也不常用,但关键时刻装装x,炫炫技,也不是不行
在这里插入图片描述

找出只出现一次的数字(I)

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

根据我们前文提到的异或操作的性质,异或满足交换律和结合律,0^N=N,N^N=0,所以将所有的数组中所有的数字异或起来,出现次数为偶数次的异或结果为0,只出现一次的数与0异或结果为本身

 public int singleNumber(int[] nums) {
 //非空数组所以不需要判断数组长度情况
        int res = 0;
        for(int i = 0;i<nums.length;i++){
            res = nums[i]^res;
        }
        return res;
    }

找出二进制位最右侧的“1”

假如一个二进制数00001111010101000,我们怎么找到这个标黄的1呢?
先说结论:N&(~N+1)

public static void main(String[] args) {
        int num = 72;//1001000
        num = num&(~num+1);//1000
        System.out.println(Integer.toBinaryString(num));
    }

在这里插入图片描述

只出现一次的数组(II)

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

这个进阶题目比一要难上许多,有两个值不相等的元素只出现一次,要用到上面刚刚介绍到的找出最右侧一算法

这个题的思路是什么呢?

一、将所有元素异或,假设两个不同的元素为a,b,那么最后结果一定是res = a^b
二、res一定是不为0的(如果为0,说明两元素相同),那么就表明一定有二进制位为1,我们假设这个二进制位是第6位(随意假设),那么a和b的第六位一定是不同的,也就是一个为0,一个为1
三、我们将第六位按照0或1分为两组,那么a,b一定在不同的组中,其他的两两相同的数一定分到一个组中
四、我们在一个组中将所有的数异或一下,假如我们在a组异或,那么剩下的数就是a,根据第一步得到的a^b,我们令res在异或一下a得到的就是b
五、刚刚的第六位是假设出来的,目的是得到一个二进制位为1的位,所以利用上述方法,可以得到一个最右侧的一位
在这里插入图片描述

public static int[] singleNumber(int[] nums) {
        int eor = 0;
        for(int i = 0;i<nums.length;i++){
            eor = eor^nums[i];
        }
        //eor = a^b
        //提取出最右侧1
        int rightOne = eor&(~eor+1);
        int a = 0;//计算出a
        for(int i = 0;i<nums.length;i++){
            if((nums[i]&rightOne)!=0){
                //说明这个是假设位是0的那一组
                a = a^nums[i];
            }
        }
        int b = eor^a;
    }
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值