• 异或运算和相关例子


    异或运算 ^:相同为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. // a 值为 甲, b值为 乙
    2. a = a^b // a= 甲^乙, b= 乙
    3. b = a^b // a= 甲^乙, b= 甲^乙^乙 = 甲
    4. a = a^b // a= 甲^乙^甲 = 乙, b= 甲

    示例:

    1. public static void main(String[] args) {
    2. int[] arr = {4,7,244,5,2};
    3. int i= 1,j=2;
    4. swap(arr,i,j);
    5. System.out.println(arr[i]+","+arr[j]);
    6. }
    7. //i 与 j 不能相同
    8. //对于引用来说,如果两个指针的地址指向同一个内存地址,那两个相同异或则会为0
    9. public static void swap(int[] arr,int i,int j){
    10. arr[i] = arr[i] ^ arr[j];
    11. arr[j] = arr[i] ^ arr[j];
    12. arr[i] = arr[i] ^ arr[j];
    13. }
    14. //结果
    15. 244,7

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

    思路一:

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

    思路二:

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

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

    相当于:

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

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

    1. public static void printOddTimesNum1(int[] arr){
    2. int eor = 0;
    3. for(int a:arr){
    4. eor ^= a;
    5. }
    6. System.out.println(eor);
    7. }
    8. public static void main(String[] args) {
    9. int [] arr = {1,4,3,4,2,1,3};
    10. printOddTimesNum1(arr);
    11. }
    12. //结果
    13. 2

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

    思路: a & (-a)

    与&运行:相同为1

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

    1. a = 00110101000
    2. # -a = a取反加1
    3. -a = ~a + 1
    4. -a = 11001010111 + 1 = 11001011000
    5. 所以
    6. a = 00110101000
    7. -a = 11001011000
    8. a & -a = 00000001000

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

    思路:

    1.

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

    2.

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

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

    3.

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

    4.

    得到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:
    1. public static void printOddTimesNum2(int[] arr) {
    2. int eor = 0;
    3. for(int x:arr){
    4. eor ^= x;
    5. }
    6. //此时eor = a ^ b
    7. //最右侧为1
    8. int rightOne = eor & -eor;
    9. int eor2 = 0;
    10. //查找ringthOne位为1的进行异或操作,得到a
    11. for (int x : arr){
    12. if((x & rightOne) != 0){
    13. eor2 ^= x;
    14. }
    15. }
    16. int a = eor2;
    17. int b = eor ^ eor2;
    18. System.out.println(a+","+b);
    19. }
    20. public static void main(String[] args) {
    21. int [] arr1 = {6,10,12,12,12,12,4,4,3,3};
    22. printOddTimesNum2(arr1);
    23. }
    24. //结果
    25. //6,10

    5 km次

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

    M > 1,  K < M

    找到,出现了K次的数,

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

    思路:

    1.

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

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

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

    2.

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

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

    3.

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

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

    code:
    1. public static void km1(int [] arr,int k,int m){
    2. int [] t = new int[32];
    3. //遍历arr数组
    4. for (int x : arr){
    5. //对每一元素的每一位检查,如果为1则t[i]计数增加1
    6. for(int i=0;i<32;i++){
    7. //x右移i位则将待检查位置移动到最右侧,与1进行与运算,如果该位置为1,则结果为1,如果该位置为0则结果为0
    8. if((x >> i & 1) != 0){
    9. t[i]++;
    10. }
    11. }
    12. }
    13. int ans = 0;
    14. //对t数组计算
    15. for (int i = 0; i < 32; i++) {
    16. //如果出现次数无法被m整除,说明出现k次的数在该位出现
    17. if(t[i] % m != 0){
    18. //则将该位记录,最终32位统计完成后,则为该数
    19. // 1 左移i位添加到ans中
    20. ans |= 1 << i;
    21. }
    22. }
    23. System.out.println(ans);
    24. }
    25. public static void main(String[] args) {
    26. //k = 2,m = 4
    27. int [] arr = {6,6,10,10,10,10,7,7,7,7,11,11,11,11,0,0,0,0,-1,-1,-1,-1};
    28. km1(arr,2,4);
    29. }
    30. //结果
    31. // 6

  • 相关阅读:
    Java的两大、三类代理模式
    【ESP32之旅】ESP32-S3 Jlink调试
    Mathorcup数学建模竞赛第四届-【妈妈杯】C题:面向多层次需求的西安旅游线路优化设计(lingo代码实现)
    从0实战一个 vue3+ ts+element-plus 项目
    Java.lang.Class类 isAnonymousClass()方法有什么功能呢?
    SpringBoot-SpringBoot中文文档
    Mysql深入优化 (一) --- 索引、视图、存储过程、触发器
    Java面试题:请谈谈Java中的volatile关键字?
    SpringBoot学习笔记(3)——B站动力节点
    每日一题 1333. 餐厅过滤器(中等)
  • 原文地址:https://blog.csdn.net/hey_lie/article/details/132691174