「这是我参与2022首次更文挑战的第27天,活动详情查看:2022首次更文挑战」。
一、认识异或运算
^ 相同为0,不同为1:可以理解为无进位相加
异或的特性:
- 0 ^ N = N
- N ^ N = 0
- 满足交换律(a^b = b^a)和结合律((a^b)^c) = a^(b^c))
偶数个1相加一定为0,奇数个1相加一定为1
二、异或运算练习
-
如何不用额外变量交换两个数
a = a ^ b b = a ^ b c = a ^ b -
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
- 利用哈希表,每一种数做词频统计,看哪种数出现了奇数次,需要额外的空间复杂度
- 利用异或运算,遍历一遍,只需要一个有限的变量就能找到哪种数出现了奇数次
// arr中,只有一种数,出现奇数次 public static void printOddTimesNum1(int[] arr) { int eor = 0; for (int i = 0; i < arr.length; i++) { eor ^= arr[i]; } System.out.println(eor); } -
怎么把一个int类型的数,提取出最右侧的1来
a & (~a + 1) = a & (-a)a &(取反 + 1)等于 a & 相反数
取反加1就是自己的相反数
a & (~a + 1) = a & (-a) -
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
分析:假设a、b这两种数出现了奇数次,则a≠b,从头到尾遍历一遍进行异或,则eor = a^b,因为a不等于b,则eor一定不等于0,说明eor在某个位置上一定有1的状态,找哪个1位置呢,哪个位置都行,那就提取最右侧的1,假设在某个位置上是最右侧的1,说明a,b在这个位置上一定是不一样的,因为一样的话,异或则为0。假设a最右侧位置上有1,b则最右侧位置上为0,怎么把a和b分开,
rightOne :00000000000001000,依次判断每个数&rightOne,不等于0,则说明这个位置上有1,又因为偶数异或为0,所以左右就找到其中一个奇数,那么另外一个奇数则再异或下。// arr中,有两种数,出现奇数次 public static void printOddTimesNum2(int[] arr) { int eor = 0; for (int i = 0; i < arr.length; i++) { eor ^= arr[i]; } // a 和 b是两种数 // eor != 0 // eor最右侧的1,提取出来 // eor : 00110010110111000 // rightOne :00000000000001000 int rightOne = eor & (-eor); // 提取出最右的1 int onlyOne = 0; // eor' for (int i = 0; i < arr.length; i++) { // arr[1] = 111100011110000 // rightOne= 000000000010000 if ((arr[i] & rightOne) != 0) { onlyOne ^= arr[i]; } } System.out.println(onlyOne + " " + (eor ^ onlyOne)); } -
一个数组中有一种数出现K次,其他数都出现了M次,M > 1, K < M,找到出现了K次的数,要求额外空间复杂度O(1),时间复杂度O(N)
分析:额外空间复杂度O(1),堵死了哈希表,可以利用长度为32的int数组,所有的数换算成二进制,所有数字二进制上的每个位置上的1全累加到数组中,
// 请保证arr中,只有一种数出现了K次,其他数都出现了M次 public static int onlyKTimes(int[] arr, int k, int m) { int[] t = new int[32]; // 额外空间复杂度O(1) // t[0] 0位置上的1出现了几个 // t[i] i位置上的1出现了几个 for (int num : arr) { // O(N) for (int i = 0; i <= 31; i++) { // 固定次数 t[i] += (num >> i) & 1; // if (((num >> i) & 1) != 0) { // t[i]++; // } } } int ans = 0; // 干干净净,往上填答案,构建答案 for (int i = 0; i < 32; i++) { // 固定次数 if (t[i] % m != 0) { // 说明k在i位上,有1 ans |= (1 << i); } } return ans; }
三、总结
异或也称为无进位相加
0 ^ N = N
N ^ N = 0
提取最右侧的1:a ^ (-a)