不使用额外变量,进行两个数的交换
异或运算:
1、 两个数做异或运算,等于两个数的二进制位做无进位相加;
2、 一个数自己跟自己异或,结果为0(N^N==0);
3、 任何数跟0异或,结果为自己本身(N^0==N);
/**
* 不使用额外变量,进行两个数的交换
*/
public class BitOperation01 {
public static void main(String[] args) {
int a = 2;
int b = 3;
a = a ^ b; // a = a ^ b
b = a ^ b; // b = a ^ b ^ b = a
a = a ^ b; // a = a ^ b ^ a = b
System.out.println(a);
System.out.println(b);
}
}
一个数组中,一种数出现了奇数次,其余数出现偶数次,找到并打印这种数
只要把所有的数异或在一起,得出的结果就是那个出现了奇数次的数。
因为一组数的异或得出的结果总是相同的,与这组数的排序无关。
而一个数和自己异或的结果为0,和0异或的结果是自己,所以所有数异或在一起,相当于相同的数两两异或,剩下一个出现奇数次的数。
/**
* 一个数组中,一种数出现了奇数次,其余数出现偶数次,找到并打印这种数
*/
public class BitOperation02 {
public static void main(String[] args) {
int[] arr = {
1,4,6,1,6,8,4,9,9};
int eor = arr[0];
for (int i = 1; i < arr.length; i++) {
eor ^= arr[i];
}
System.out.println(eor);
}
}
因为相同的数做异或运算,得出的结果是0,而任何数跟0做异或运算,得出的结果都是该数自身,所以遍历数组中所有的数,进行异或运算,最后得出的结果就是出现了奇数次的数。
怎么把一个整形最右侧的1提取出来?
一个数与上自己的取反加1,就能提取出最右侧的1:a & (~a + 1) = a & -a
一个数组中,两种数出现了奇数次,其余数出现偶数次,找到并打印这两种数
假设出现奇数次的数是 a ^ b
1、 先把所有的数异或起来,得到的结果是a^b;
2、 然后提取出最右侧的1;
3、 把这一位为1的数异或在一起,就可以得到其中一个出现奇数次的数;
4、 再把结果异或上a^b,则得到另一个出现奇数次的数;
原理: 因为a ^ b 的最右侧为1的位置,a和b在这一位上的值是不一样的(否则就不会异或后这一位为1),那么把所有的数分两组:这一位为1的和这一位不为1的,a和b比被分到不同组,而组中其他数又是出现偶数次的,异或起来后就是0,那么剩下的就是出现奇数次的数。
/**
* 一个数组中,两种数出现了奇数次,其余数出现偶数次,找到并打印这两种数
*/
public class BitOperation03 {
public static void main(String[] args) {
int[] arr = {
1,4,6,1,6,8,9,9}; //4和8出现奇数次,现在要找出4和8
int eor = arr[0];
for (int i = 1; i < arr.length; i++) {
eor ^= arr[i];
}
//此时eor为 4 ^ 8
//两个不等的数异或运算,得出的结果必然不等于0,取出eor最右侧的1
eor = eor & (~eor + 1);
//此时可以遍历数组,把数组分两组:1、和eor最右侧1相同二进制位上为1的;2、和eor最右侧1相同二进制位上为0的
//此时出现奇数次的两个数,必然落在不同组,分别对这两组数进行异或运算,得出两个出现奇数次的数
int a = 0;
int b = 0;
for (int i = 0; i < arr.length; i++) {
if ((arr[i] & eor) == 0) {
a ^= arr[i];
} else {
b ^= arr[i];
}
}
System.out.println(a);
System.out.println(b);
}
}
一个数组中,所有数都出现m此,只有一种数出现k次,找到并返回这种数
一个数组中,所有数都出现m此,只有一种数出现k次,找到并返回这种数
m>1,k < m
要求空间复杂度O(1),时间复杂度O(n)
思路:
1、 声明一个32长度的整形数组bits(因为是固定长度的,所以空间复杂度还是O(1));
2、 将每一个数的每一位累加到数组bits对应位置上;
3、 然后数组每一个位数都模m,如果模完之后,该位上的数不为0,代表这个出现k次的数的对应的bit位为1;
/**
* 一个数组中,所有数都出现m此,只有一种数出现k次,找到并返回这种数
* m > 1,k < m
* 要求空间复杂度O(1),时间复杂度O(n)
* Created by huangjunyi on 2022/11/19.
*/
public class BitOperation04 {
public static int findKTimes(int[] nums, int k, int m) {
// 一个32位的整形数组,存储每一个数的的每一个bit位相加
int[] bits = new int[32];
for (int num : nums) {
for (int i = 0; i < 32; i++) {
bits[i] += ((num >> i) & 1);
}
}
int res = 0;
for (int i = 0; i < bits.length; i++) {
// 如果一个bit位上,% m 后,不为0,代表这一位上,出现k次的数是1
if (bits[i] % m != 0) res |= (1 << i);
}
return res;
}
}