异或运算(高频必会面试题)

异或

一.运算

异或运算:相同为0,不同为1。即**无进位相加 **

int a = 7;
int b = 13;
a ^ b = 10;
//转为二进制形式运算
// a : 0 0 1 1 1 = 7
// b : 0 1 1 0 1 = 13
//a^b: 0 1 0 1 0 = 10

二.性质

  • 0 ^ N = N
  • N ^ N = 0
  • 交换律:a ^ b = b ^ a
  • 结合律:( a ^ b ) ^ c = a ^ ( b ^ c )
  • 由交换律和结合律可以推出:同样一批数不管以何种先后顺序异或在一起结果相同,a ^ b ^ c ^ d = c ^ d ^ b ^ a = F

三.常见面试题

题目一:如何不用额外变量交换两个数

  • 正常解法:
int a;
int b;
int tmp = a;
a = b;
b = a;
  • 异或运算
int a;
int b;
a = a ^ b;  // (1)式
b = a ^ b;  //代入(1) 由 N ^ N = 0和结合律可得 b = a ^ (b ^ b) = a;
a = a ^ b;  //代入(1) 由 N ^ N = 0和结合律,交换律可得 a = a ^ b ^ a = (a ^ a) ^ b = b;

注意:用于数组中两数交换时需保证 arr[a] 和 arr[b] 的地址不同,a != b。数值相同可以使用。

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

  • 正常解法:
    • 思路:使用哈希表进行词频统计,最后判断哪个数出现了偶数次。
  • 异或运算:
// 由性质:0 ^ N = N 和 N ^ N = 0 可知 
// 出现偶数次的数都会因 N ^ N = 0而消掉,最后出现奇数次的数因 0 ^ N = N 而保留下来
int[] arr = {1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5}
int num = 0;
for (int i = 0; i < arr.length; i++) {
    num ^= arr[i];
}
System.out.println(num); // 2

题目三:怎么把一个int类型的数,提取出最右侧的1来

可知 a & ( - a) = a & ( ~ a + 1) 即 - a = ~ a + 1

  a     = 0 1 1 0 1 1 1 0 0 1 0 0 0 0
~ a     = 1 0 0 1 0 0 0 1 1 0 1 1 1 1 // ~ 取反
~ a + 1 = 1 0 0 1 0 0 0 1 1 1 0 0 0 0
a & (-a)= 0 0 0 0 0 0 0 0 0 1 0 0 0 0 // 令 a & (-a) 即可提取出

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

int[] arr = {1, 1, 3, 3, 4, 4, 5, 6, 6, 7, 7, 7};
int eor = 0;
//得到 eor = a ^ b
for (int i = 0; i < arr.length; i++) {
    eor ^= arr[i];
}
// 由于 a 和 b 是两种数
// 所以 eor != 0
// 提取 eor 最右侧的1,由异或‘相同为0,不同为1’ 可知在该位置 a 和 b 不同时为 1
int rightOne = eor & (~eor + 1);
int onlyOne = 0;
// 根据最右侧eor为1位置的 按是否为1 异或出一组数 
for (int i = 0; i < arr.length; i++) {
    //   arr[i] = 111100011110000
    // rightOne = 000000000010000
    if ((arr[i] & rightOne) != 0) {
        onlyOne ^= arr[i];
    }
}
// 可得到 onlyOne = a 或 b , eor ^ onlyOne = a ^ b ^ a(或者b),由此可区分两个数
System.out.println(onlyOne + " " + (eor ^ onlyOne));

题目五:一个数组中有一种数出现了 K 次,其他数都出现了 M 次,M > 1, K < M ,找到出现了 K 次的数,要求,额外空间复杂度O(1),时间复杂度O(N)

int[] arr = {1, 1, 1, 1, 4, 4, 4, 4, 2, 2, 2, 2, 8, 8, 8};
int k = 3;
int m = 4;
// 以二进制形式记录所有位次出现次数
int[] t = new int[32];
// 遍历统计
for (int num : arr) {
    for (int i = 0; i <= 31; i++) {
        //  if (((num >> i) & 1) != 0) {
        //      t[i]++;
        //  }
       	//如果不为0 , 对应位为+1
        t[i] += (num >> i) & 1;
    }
}
// 记录结果
int ans = 0;
for (int i = 0; i < 32; i++) {
    // 模 m 后 位次不为0,由于k < m,说明 目标数在该位置也出现了k次
    if (t[i] % m != 0) {
        // 通过 | 运算将 1 或入对应位
        ans |= (1 << i);
    }
}
System.out.println(ans);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

观止study

希望我的经验能为你指路

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值