异或运算
概念:
- 异或运算:相同为0,不同为1
- 同或运算:相同以1,不同为0
- 总结:异或运算 = 无进位相加
性质:
- 0^N == N
- N^N == 0
- 满足交换律和结合律
-
如何不用额外变量交换两个数
灵活运用异或的两个性质,如果是数组交换的话必须保证互换的两个索引值不相同
public static void main(String[] args) { int a = 19; int b = 6000; System.out.println(a); System.out.println(b); // 不用额外变量交换两个数 // 异或:不进位相加 a = a ^ b; b = a ^ b; // b = a ^ b ^ b = a ^ 0 = a a = a ^ b; // a = a ^ b ^ a = 0 ^ b = b System.out.println(a); System.out.println(b); } // 注意: i 和 j不能相同,如果相同的话就全是0了 public void swap(int[] arr, int i, int j) { arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; }
-
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
定义返回结果ans,遍历数组进行异或则可得到出现奇数次的数
// 原理: 使用的也是异或的性质 例: 11 3333 55 66 7777 8 // 只要是偶数个,那么就为0,如果只有一个奇数则说明其他的都是偶数,那么他们的异或最终为0然后再和奇数异或,最终得到这个奇数 public static int printOddTimesNum1(int[] arr) { int ans = 0; for (int i = 0; i < arr.length; i++) { ans ^= arr[i]; } return ans; }
-
一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
-
遍历数组进行异或获得最终结果,也就是a^b
-
算出a^b这个数的最右侧的1
(a&(~a+1),意味着a,b在这一位是不同的)
-
遍历数组,判断当前数是否有最右侧的数,如果有则进行异或处理,最终得到的则是这两个数中的一个
-
得到的数与a^b进行异或则获得另外一个数
public static void prinOddTimesNum2(int[] arr) { int eor = 0; for (int i = 0; i < arr.length; i++) { eor ^= arr[i]; } // 最终res = a ^ b,因为a,b是两种数所以eor != 0 // 取res最右侧的1,代表着a,b的这一位是不相同的因为异或不同才为1 // 例: 00110010110111000 // rightOne: 00000000000001000 int rightOne = eor & (~eor + 1); // 这里就将a,b分离开了 int onlyOne = 0; 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)
-
自定义一个数组记录数组中每一个数每一位出现的次数
-
遍历数组,每个数的每一位是否存在,如果存在就加在对应的位数上
-
定义返回的结果值ans,遍历自定义的数组,拿到数组中对应的值与m进行模运算,如果等于0就将对应位的值赋值到ans中
public static int onlyTimes(int[] arr, int k, int m) { // 用数组来表示每位的个数 int[] t = new int[32]; // t[0] 0位置的1出现了几个 // t[i] i位置的1出现了几个 for (int num : arr) { for (int i = 0; i < 32; i++) { // 不为0,则num在i位上为1 // if (((num >> i) & i) != 0) { // t[i]++; // } // 优化:如果为0,加了个寂寞 t[i] += (num >> i) & 1; } } int ans = 0; // 看每一位上的数与m取模是否为0,如果不为0则说明有k // 分析:如果其他数出现m次,那么应该为0,不为0则说明有出现K次的数 for (int i = 0; i < 32; i++) { if (t[i] % m != 0) { // 第i位上有1 ans |= (1 << i);// 把1设置上去 } } return ans; }
-
-
KM题扩展:如果另一个数不为K次,则结果返回-1
public static int onlyTimes2(int[] arr, int k, int m) { // 用数组来表示每位的个数 int[] t = new int[32]; // t[0] 0位置的1出现了几个 // t[i] i位置的1出现了几个 for (int num : arr) { for (int i = 0; i < 32; i++) { t[i] += (num >> i) & 1; } } int ans = 0; // 看每一位上的数与m取模是否为0,如果不为0则说明有k // 分析:如果其他数出现m次,那么应该为0,不为0则说明有出现K次的数 for (int i = 0; i < 32; i++) { // 模完等于0,说明这一位不含有1 if (t[i] % m == 0) { continue; } // 出现k次才统计进去 if (t[i] % m == k) { ans |= (1 << i); } else {// 出现的不是k次 return -1; } } if (ans == 0) { int count = 0; for (int num : arr) { if (num == 0) { count++; } } if (count != k) { return -1; } } return ans; }
-
对数器(以最后一道题为例)
//对数器
public static void main(String[] args) {
int testTimes = 10000;
int maxValue = 100;
int kinds = 10;
int k = 8;
int m = 14;
System.out.println("test begin");
for (int i = 0; i < testTimes; i++) {
int[] arr = generateArr(kinds, k, m, maxValue);
int orgin = onlyTimes(arr, k, m);
int test = test(arr, k);
if (orgin != test) {
System.out.println("fail");
}
}
System.out.println("test end");
}
public static int test(int[] arr, int k) {
Map<Integer, Integer> map = new HashMap<>();
for (int num : arr) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
for (Integer num : map.keySet()) {
if (map.get(num) == k) {
return num;
}
}
return -1;
}
// 一个数组中有一种数出现K次,其他数都出现了M次, M > 1, K < M 找到,出现了K次的数, 要求,额外空间复杂度O(1),时间复杂度O(N)
private static int[] generateArr(int kinds, int k, int m, int maxValue) {
int len = (k * 1) + ((kinds - 1) * m);
int[] arr = new int[len];
int index = 0;
int kTimesValue = (int) (Math.random() * (maxValue + 1)) - (int) (Math.random() * (maxValue + 1));
for (; index < k; index++) {
arr[index] = kTimesValue;
}
kinds--;
HashSet<Integer> set = new HashSet<>();
set.add(kTimesValue);
while (kinds != 0) {
int curValue = 0;
do {
curValue = (int) (Math.random() * (maxValue + 1)) - (int) (Math.random() * (maxValue + 1));
} while (set.contains(curValue));
set.add(curValue);
kinds--;
for (int i = 0; i < m; i++) {
arr[index++] = curValue;
}
}
for (int i = 0; i < arr.length; i++) {
int j = (int) Math.random() * arr.length;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
}