1异或运算法则
异或运算:相同为0,不同为1
异或运算就记成无进位相加。
满足交换律,结合律。
0^N = N
N^N = 0
int a = 7; // 00111
int b = 13; // 01101
01010 = 8+2 = 10
2不用任何空间的情况下交换两个变量
内存区域不同,可以使用这个方法:
int a = 13;
int b = 7;
a = a^b;
b = a^b;
a = a^b;
---> a = 7; b = 13;
解释:
a = a^b; // a = a^b; b = b;
b = a^b; // a = a^b; b = a^b^b = a^ (b^b) = a^0 = a;
a = a^b; // a = a^b^a = a^a^b = 0^b = b;
3一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
相同为0,交换律。
[a, b, c, d, e, ...]
int eor = 0;
eor = eor^a;
eor = eor^b;
eor = eor^c;
eor = eor^d;
...
eor = eor^...;
4怎么把一个int类型的二进制数,提取出最右侧的1
a = 01101110010000
ans= 00000000010000
a = 01101110010000
~a= 10010001101111
~a+1=10010001110000
a & (~a+1)
=a & (-a)
= 00000000010000
5一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数(奇数次)
这两种奇数为a,b:
eor = 0;
eor = eor^...
= a^b != 0; //因为a != b;
所以:eor的某一位状态为1,利用算法第四题找到这个状态位1。
所以,可以将所有的数分为:
1、最右侧的是1,
2、最右侧的是0: 两种。
eor1 = 0; eor1 ^ ...(第一种的数) = a(假设);
eor^eor1 = b;
// arr中,有两种数,出现奇数次
public static void print0ddTimesNum2(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 : 000000000001000
int rightOne = eor & (-eor); //提取出最右的1
int onlyOne = 0; // eor'
for (int i = 0 ; i < arr.length; i++) {
// arr[1] = 111100011110000
// rightOne= 00000000010000
if ((arr[i] & rightOne) != 0) {
onlyOne ^= arr[i];
}
}
System. out. println(onlyOne +" " + (eor ^ onlyOne));
}
6 一个数组中有一种数出现K次,其他数出现了M次(M>1,K<M),找到出现了K次的数,要求:额外空间复杂度0(1)
定义一个int[] t = new int[32]; //存储二进制的每一位数
将数组中的每一个数转为二进制表示,t数组的每一位累加;
累加完成之后,数组每一位与M 作% 运算;
不是0的位,则为出现k次的数二进制位1的位。
package entity;
/**
* @email [email protected]
* @author: zhaoshuai
* @date: 2021/8/8 7:15
*/
public class Soultion {
public static void onlyKTimes(int[] arr, int k, int m) {
int[] t = new int[32];
// t[0]:0位置的1出现了几个
for (int num : arr) {
for (int i = 0; i <= 31; i++) {
// 依次提取出每一位的状态
if (((num >> i) & 1) != 0) {
t[i]++;
}
}
}
int ans = 0;
for (int i = 0; i < 31; i++) {
if (t[i] % m != 0) {
ans |= (1 << i);
}
}
System.out.println(ans);
}
}