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

导读:本篇文章讲解 异或运算(高频必会面试题),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

异或

一.运算

异或运算:相同为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);

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/82599.html

(0)
小半的头像小半

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!