异或运算和相关例子

异或运算 ^:相同为0,不同为1【无进位相加】

与运算 &:相同为1,不同为0

或运算 |:有1为1

异或运算的性质

  1. 0^N == N N^N == 0
  2. 异或运算满足交换律和结合律

1. 0^N == N

一个数对0进行异或,则结果为自身

如: 0 ^ 5 = 5. 0 ^ 1 = 1

0000 ^ 0101 = 0101

2 N^N == 0

一个数对自己进行异或,则结果为0

如:5^5 = 0

0101 ^ 0101 = 0000

3 异或运算满足交换律和结合律

a^b^c = a^(b^c) = a^c^b

结合使用比如:

3^5^2^5^3 = (3^3)^(5^5)^2 = 0^0^2 = 2

例子

1 如何不用额外变量交换两个数

java 复制代码
// a 值为 甲, b值为 乙
a = a^b // a= 甲^乙, b= 乙
b = a^b // a= 甲^乙, b= 甲^乙^乙 = 甲
a = a^b // a= 甲^乙^甲 = 乙, b= 甲

示例:

java 复制代码
public static void main(String[] args) {
        int[] arr = {4,7,244,5,2};
        int i= 1,j=2;
        swap(arr,i,j);
        System.out.println(arr[i]+","+arr[j]);
}

//i 与 j 不能相同
//对于引用来说,如果两个指针的地址指向同一个内存地址,那两个相同异或则会为0
public static void swap(int[] arr,int i,int j){
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
}


//结果
244,7

2 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数

思路一:

使用hashmap,遍历数组,key为数组值,value为计数,最后遍历hashmap,找到基数value,返回key为该数。

思路二:

相同数进行异或值为0,0与其他数异或值为其他数。

定一个变量 eor = 0,分别与数组中每一个数进行异或

相当于:

0 ^ arr[0] ^ arr[1] ... arr[n-1]

由于数组是特殊的,只有一种数出现基数次,其余为偶数次,那么偶数次相同数进行异或都为0,最后剩下一个基数次的数

java 复制代码
 public static void printOddTimesNum1(int[] arr){
        int eor = 0;
        for(int a:arr){
            eor ^= a;
        }
        System.out.println(eor);
}
public static void main(String[] args) {
        int [] arr = {1,4,3,4,2,1,3};
        printOddTimesNum1(arr);
}


//结果
2

3 怎么把一个int类型的数,对于二进制 提取出最右侧的1来

思路: a & (-a)

与&运行:相同为1

-a 在二进制中表示为 取反 + 1

bash 复制代码
a = 00110101000
# -a = a取反加1
-a = ~a + 1 
-a = 11001010111 + 1 = 11001011000

所以
a =      00110101000
-a =     11001011000
a & -a = 00000001000

4 一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数

思路:

首先参照2,先将偶数次的数消掉,即 eor = eor ^ arr,之后得到的就是两个基数进行异或的结果,假设这两个出现基数的数为a和b,这eor = a^b,并且因为a 不等于 b,所以eor也不等于0

此时,eor为 a ^b,由于a 不等于b,观察eor的二进制形式,其中有1的位一定为a和b不相同的位置【因为异或为不同位为1,相同位为0】,指定最右侧为1位作为分界线,计算得rightOne,

假设最右侧位为第3位,则数组中的数可以分为两类,一类位第3位为1,记为左侧,一类位第3为为0,记为右侧,且a与b一定不在同一侧,假设a在左侧,b在右侧

接着,即eor2 = 0,异或左侧数据,则得到 a,此时eor2 = a【左侧中只有一个是基数项为a,其他偶数项被消掉 】

得到a后,b = eor ^ eor2[此时为eor = a ^b, eor2 = a,b = a^b ^a =b]

举例:

arr = [6,10,4,4,12,12,12,12,3,3] //此时出现基数次的为6,10

6 = 00110

10= 01010

eor = 6 ^ 10 = 01100

得到最右侧为1的数为00100

划分左侧即第三位为1的数有6,4,12,第三位为0的数有10,3

eor2 = 0 ^ 6 ^4^4^12^12^12^12 = 6

b = eor ^ eor2 = 6^10^6 = 10

得到两个基数次的数为6和10

code:
java 复制代码
    public static void printOddTimesNum2(int[] arr) {
        int eor = 0;
        for(int x:arr){
            eor ^= x;
        }
        //此时eor = a ^ b
        //最右侧为1
        int rightOne = eor & -eor;
        int eor2 = 0;

        //查找ringthOne位为1的进行异或操作,得到a
        for (int x : arr){
            if((x & rightOne) != 0){
                eor2 ^= x;
            }
        }

        int a = eor2;
        int b = eor ^ eor2;
        System.out.println(a+","+b);
    }

    public static void main(String[] args) {

        int [] arr1 = {6,10,12,12,12,12,4,4,3,3};
        printOddTimesNum2(arr1);
    }
    
    //结果
    //6,10

5 km次

一个数组中有一种数出现K次,其他数都出现了M次,

M > 1, K < M

找到,出现了K次的数,

要求,额外空间复杂度O(1),时间复杂度O(N)

思路:

准备一个长度为32的整形数组 t,遍历arr,将每一个元素的所以位为1的位置统计到t中,如元素a的第3位为1,则t[2]++,第9位为1,则t[8]++

t[0] 0位置的1出现了几个

t[i] i位置的1出现了几个

对t数组每一个元素,如果对 M取模不为0,则说明出现k次的数在该为为1【因为如果该位能被7整除,说明该位的1出现次数为整数次】

设置一个变量ans = 0,遍历t,如果元素对M取模不为0,t[i] %M != 0,则 ans的第i位设置为1

如何将第i位设置到ans上呢

将1左移i位,则i位置位1,其余位置为0,再 或运算到ans上,i位置变为1,其余位置保持不变

code:
java 复制代码
    public static void km1(int [] arr,int k,int m){
        int [] t = new int[32];
        //遍历arr数组
        for (int x : arr){
            //对每一元素的每一位检查,如果为1则t[i]计数增加1
            for(int i=0;i<32;i++){
                //x右移i位则将待检查位置移动到最右侧,与1进行与运算,如果该位置为1,则结果为1,如果该位置为0则结果为0
                if((x >> i & 1) != 0){
                    t[i]++;
                }
            }
        }
        int ans = 0;
        //对t数组计算
        for (int i = 0; i < 32; i++) {
            //如果出现次数无法被m整除,说明出现k次的数在该位出现
            if(t[i] % m != 0){
                //则将该位记录,最终32位统计完成后,则为该数
                // 1 左移i位添加到ans中
                ans |= 1 << i;
            }
        }
        System.out.println(ans);
    }

    public static void main(String[] args) {
        //k = 2,m = 4
        int [] arr = {6,6,10,10,10,10,7,7,7,7,11,11,11,11,0,0,0,0,-1,-1,-1,-1};
        km1(arr,2,4);
    }

    //结果
    // 6
相关推荐
苹果醋335 分钟前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader1 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭1 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0072 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
ssr——ssss2 小时前
SSM-期末项目 - 基于SSM的宠物信息管理系统
java·ssm
一棵星2 小时前
Java模拟Mqtt客户端连接Mqtt Broker
java·开发语言
鲤籽鲲2 小时前
C# Random 随机数 全面解析
android·java·c#
xiaoshiguang33 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode