一 前置 !<<<、 >>> 、|=、&、^区别
二进制数字各种运算!<<<、 >>> 、|=、&、^区别
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A= 1100 0011
(A&B),得到12,即0000 1100
按位与运算符(&): 如果相对应位都是1,则结果为1,否则为0;
(A | B)得到61,即 0011 1101
按位或运算符(|): 如果相对应位都是0,则结果为0,否则为1;
(A ^ B)得到49,即 0011 0001
异或运算符(^): 如果相对应位值相同,则结果为0,否则为1;
(〜A)得到-61,即1100 0011
按位取反运算符(〜): 翻转操作数的每一位,即0变成1,1变成0;
A << 2得到240,即 1111 0000
按位左移运算符(<<): 左操作数按位左移右操作数指定的位数。
A >> 2得到15即 1111
按位右移运算符(>>): 左操作数按位右移右操作数指定的位数。
A>>>2得到15即0000 1111
按位右移补零操作符(>>>): 左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。
二 概念
异或运算:相同为0,不同为1
同或运算:相同以1,不同为0
或者理解无进位相加
三 异或运算的性质
0^N == N N^N == 0
异或运算满足交换律和结合率
四 交换两个数-如何不用额外变量交换两个数
4.1 思路分析:利用 异或 N^N == 0 这个特点
4.2 代码实例
int a = 16;
int b = 603;
System.out.println(a);
System.out.println(b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
五 查找数组中出现奇数次的数
5.1 思路分析:0^N == N N^N == 0
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这种数
1.4 怎么把一个int类型的数,提取出二进制最右侧的1来
5.2 代码实例
// 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);
}
六 查找数组两种数出现奇数次
一个数组中有两种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这两种数
6.1 思路分析:
1、err =0;&上数组中有的数,因为只有两种数出现基数次,其他的都是偶数次,最后&出来的结果是a^b;
2、 // a 和 b是两种数
// eor != 0
// eor最右侧的1,提取出来
// eor : 00110010110111000
// rightOne :00000000000001000
// int rightOne = eor & (-eor); // 提取出最右的1
3、将最右测是1的去全部提取出来放一个数组里面,因为其他数都是偶数次,所以就找到a或者b中其中为1的数
4 宁一个数就等于 eor ^ onlyOne
6.2 代码实例
// 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次
一个数组中有一种数出现K次,其他数都出现了M次,
M > 1, K < M
找到,出现了K次的数,
要求,额外空间复杂度O(1),时间复杂度O(N)
7.1 思路分析
1、申请一个int型号的32数组 int[] help = new int[32];
2、将数组中每一个数的二进制对应位置出现的数记录到数组的对应位置;
3、在数组的每一个位置计算当前位置出现1的次数,help[i] %= m; 如果help[i]等于0,说明出现了M次,k次的数就没有
如果help[i] !=0 ;里面有出现k次的数,然后就可以计算对应的数
ans |= 1 << i;//1向左一定i位把值|进去
7.1 代码实例
// 更简洁的写法
public static int km(int[] arr, int k, int m) {
int[] help = new int[32];
for (int num : arr) {
for (int i = 0; i < 32; i++) {
help[i] += (num >> i) & 1;
}
}
int ans = 0;
for (int i = 0; i < 32; i++) {
help[i] %= m;
if (help[i] != 0) {
ans |= 1 << i;
}
}
return ans;
}