使用异或运算的注意事项以及适用的题型

1.注意事项

使用异或运算交换数据很容易忽视的一点是:
要交换的两个变量的值可以相同,但是交换的数据不能是同一块内存区域

可以用下面的代码举个例子,有三个变量,a=2,b=2,arr={1,1}。
使用异或运算交换a和b没有出现问题,但是交换arr[0]和arr[0]却出现了问题 。如下图所示


public class Test {
    public static void main(String[] args) {
        int a=2;
        int b=2;
        System.out.println("原始数据:a="+a+", b="+b);
        a=a^b;
        b=a^b;
        a=a^b;
        System.out.println("a和b交换数据:a="+a+", b="+b);
        System.out.println("----------------------------");
        int[] arr={1,1};
        System.out.println("原始数据:arr[0]="+arr[0]);
        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]交换数据后:arr[0]="+arr[0]);
    }
}

运行结果如下图:
交换数据的运行结果

为什么会这样呢?

  1. 原因很简单,分析一下代码可以看出来:如果使用异或运算交换的是同一块内存区域,
    在执行arr[0]= arr[0] ^ arr[0];时,arr[0]这一块内存区域的值已经变成了0;
    后面再执行arr[0]=arr[0]^arr[0]时,arr[0]的值也只能一直是0;

  2. 也可以从另外一个 角度思考:使用异或交换时

        a=a^b;
        b=a^b;
        a=a^b;
  • 如果a和b指向的不是同一块内存区域,更改a的值的时候,b的值不会受到任何影响,也就是说执行完第一行代码a=a^b之后,b的值还没有任何变化
  • 但是如果a和b指向的是同一块内存区域的话,更改a的值的时候,b的值也会受到影响,这时候就出现了问题。执行完第一行代码a=a^b时,a=0,因为a和b指向的是同一块内存区域,这时候,b的值也变为了0,后面无论怎么进行异或运算,a和b永远是0;

所以,使用异或运算交换数据的时候,一定要注意,要交换的两个数据值可以相同,但是不能指向同一块内存区域。如果指向同一块 内存区域,这块 内存区域的值就变为了0

另外异或运算满足结合律和交换律,所以一组数据异或运算时,无论数据的顺序是什么样的,无论怎么结合,结果都是一样的

2.适用题型

  1. 不开辟额外的空间交换两个数字(解法:使用上述代码进行异或运算交换数据)
  2. 一个数组中只有一种数字出现了奇数次,其余的数字都出现了偶数次,请你找出这个出现奇数次的数据(解法:只需要将数组中所有的数据全部进行异或运算,然后返回结果即可)
  3. 一个数组 中 有两个数字出现了奇数次,其余数字都出现了偶数次
    解法如下 :
  • 将数组中 的所有数字进行异或,结果放在变量 a里。假设那两个出现奇数次的数字是num1和num2,这时a其实就等于num1^num2。
  • 取出a二进制形式的最右侧(任意一位都行)的一,假设是第k位为1,说明那两个出现奇数次的数字在该位一个为0,一个为1;
  • 将所有的数字分为两类,一类是第k位为1(这一类中的数字由num1和出现偶数次的其他数字),一类是第k位为0(这一类中的数字由num2和出现偶数次的其他数字).
  • 将a与其中一类异或,得到第一个出现奇数次的数字num1
  • 将a与num1异或,得到第二个出现奇数次的数字num2

代码如下:

class Solution {
    public int[] singleNumber(int[] nums) {
        int temp=nums[0];
        for(int i=nums.length-1;i>0;i--){
            temp=(temp^nums[i]);
        }   //假设两个出现奇数次的数字是num1和num2,那么temp=num1^num2;

        //取出temp最右侧的一个1,假设是在第k位
        int test=temp&((~temp)+1);
        int num1=0;
        //num1和num2的第k位一个为1,一个为0,
        for(int i=nums.length-1;i>=0;i--){
            if((test&nums[i])==test){   //将第k位为1的进行异或运算,最后得到的就是num1
                num1^=nums[i];
            }
        }
        temp=temp^num1; //temp = num1^num2^num1 = num2;
        return new int[]{temp,num1};
    }
}

作者:nan-ke-yi-meng-8r
链接:https://leetcode.cn/problems/single-number-iii/solution/by-nan-ke-yi-meng-8r-h6qn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

3.分享几个其他位运算的题目

  1. 给你任意一个数字a,请你返回一个数字b。要求:b的二进制形式只有一位为1,其他位全为0,为1的那一位是数字a二进制形式中最右侧的1所在的位置。
    例如,给你一个数字12=b’1100,返回的结果为4=0100;
    解法:b=a&(-a),返回b即可

  2. 给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。

  • 解法:
class Solution {
    public int singleNumber(int[] nums) {
        /**
         数组bit[i]表示数组中每个数的二进制形式的第i位是1的次数,
         假设第k位不是3的整数倍,那么说明返回结果(只出现一次的数字)的第k位为1
         如果第k位是三的倍数,说明返回结果的第k位为0
         */
        int[] bit=new int[32];
        for(int num:nums){
            for(int i=0;i<32;i++){
                bit[i]+=((num>>i)&1);   //如果num的第i位为1,bit【i】加1
            }
        }
        int ans=0;
        for(int i=0;i<32;i++){
            if(bit[i]%3!=0){    //第i位的数字如果不是3的倍数,说明返回结果第i位为1
               ans= (ans|(1<<i));   //将返回结果的第i位置一
            }
        }
        return ans;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值