基础知识:
异或运算:相同为0,不同为1 同或运算:相同以1,不同为0 能长时间记住的概率接近0% 所以,异或运算就记成无进位相加! 异或运算的性质 0^N == N N^N == 0 异或运算满足交换律和结合率 上面的两个性质用无进位相加来理解就非常的容易
1.如何不用额外空间交换两个数据?
public static void swap (int[] arr, int i, int j) {
// arr[0] = arr[0] ^ arr[0];
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
2. 一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
通过我们的基础知识我们可以知道如果一个数出现了偶数次那么它异或之后为0,出现奇数次的那个数就可以被筛选出来。
// 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);
}
2.1升级版: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));
}
3.怎么把一个int类型的数,提取出最右侧的1来
公式:,-error = (~error)+1
10 的二进制数: 0000 1010
10 的负数(取反+1),分两步走:
- 10 二进制取反:
1111 0101
- 10 二进制取反+1(-10):
1111 0110
将10 和 -10 进行与运算0000 1010
1111 0110
&
运算---------0000 0010
得到了 10 的二进制位最右边的 1