认识异或运算
异或运算就记成无进位相加
0 1 1
1 0 1
1 1 0
异或运算满足交换律和结合律
同样一批数,不管选择什么样的顺序做异或运算,最后结果一定是一个
如何不使用额外的变量交换两个数
1)a = a ^ b
2) b = a ^ b
3) a = a ^ b
比如说a = 甲,b = 乙
1)a = 甲 ^ 乙,b = 乙
2)b = 甲^乙^乙,a = 甲 ^ 乙
3)a = 甲^乙^甲,b = 甲
public static 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];
}
能够使用异或运算的前提是,两个数的交换,不能是同一个位置
只有两个内存区域是分开的,你才能这么玩
如果要在arr 里交换 i位置跟j位置的数, 如果i位置和j位置是同一个位置, 则用异或交换是错误的你是一个内存区域,你跑完这三行代码之后,就把自己刷成0
一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到并打印这个数
用eor异或所有人, 把最后结果留在自己身上, 这么干完一遍之后,最后你返回eor的值就是那个出现了奇数次的数
public static void printOddTimesNum1(int[] arr){
int eor = 0;
for(int i = 0;i < arr.length;i++){
eor ^= arr[i];
}
}
把一个int类型的数,提取出最右侧的1出来
int rightOne = eor & (~eor + 1);
例如:
eor = 0110 1110 0100 0000
~eor = 1001 0001 1011 1111
~eor+1 = 1001 0001 1100 0000
& = 0000 0000 0100 0000
此时的结果就是最右侧的1
一个数组中有两种数出现奇数次,其他数出现偶数次,怎么找到并打印这个数
public static void printOddTimesNum1(int[] arr){
int eor = 0;
for(int i = 0;i < arr.length;i++){
eor ^= arr[i];//此时得到的eor = a^b,偶数次都为0了,所以最后就剩a^b
}
//又因为a != b,a ^ b != 0,所以a,b两个数中肯定有一位不相同
int rightOne = eor & (~eor + 1);//提取出最右侧数
int onlyOne = 0;
//循环得到的结果是a or b
for(int i = 0;i < arr.length;i++){
if((arr[i] & rightOne) != 0){
onlyOne ^= arr[i];
}
}
sout(onluOne + (eor ^ onlyOne));
}
一个数组中有一种数出现K次,其他数都出现了M次, M > 1, K < M 找到,出现了K次的数, 要求,额外空间复杂度O(1),时间复杂度O(N)
题意
题解
准备一个长度为 32 的数组, 固定长度的数组, 依然额外空间复杂度O(1)第一步: 把所有数字二进制状态每个位置的1都累加到t里
任何一个整数都可以转成数组形式的二进制状态
如果 a, b, c, 出现7次, d出现3次, 如果第0位不是7的整数倍, 意味着d 在 0位一定是1, 如果是7的整数倍, 意味着d 在 0位一定不是1, 因为K>M
1出现7次, 3出现7次, 5出现3次, 二进制表达在第零位相加为17次, 不是7的整数倍,说明你要求的这个出现了3次的数在第零位肯定是1通过每一位上是否是7的整数倍,来把出现较小次数的数在这位是不是1确定下来
- 可以最后通过每一位上是否是 M 的整数倍这件事就把那个出现了较小次数的数给他依次给弄出来- 如果我们在某一个变量发现 1 的数量不能够被 M 整除,说明我要找的那个数,在这一位它一定是 1 状态。- 如果我们在某一个变量发现 1 的数量能够被 M 整除,说明我要找的那个数,在这一位它一定是0 状态。
如果零这个数出现K次, 则需要特殊处理
public class Code03_KM {
public static int test(int[] arr, int k, int m) {
HashMap map = new HashMap<>();
for (int num : arr) {
if (map.containsKey(num)) {
map.put(num, map.get(num) + 1);
} else {
map.put(num, 1);
}
}
for (int num : map.keySet()) {
if (map.get(num) == k) {
return num;
}
}
return -1;
}
public static HashMap map = new HashMap<>();
// 请保证arr中,只有一种数出现了K次,其他数都出现了M次
public static int onlyKTimes(int[] arr, int k, int m) {
if (map.size() == 0) {
mapCreater(map);
}
int[] t = new int[32];
// t[0] 0位置的1出现了几个
// t[i] i位置的1出现了几个
for (int num : arr) {
while (num != 0) {
int rightOne = num & (-num);
t[map.get(rightOne)]++;
num ^= rightOne;
}
}
int ans = 0;
for (int i = 0; i < 32; i++) {
if (t[i] % m != 0) {
if (t[i] % m == k) {
ans |= (1 << i);
} else {
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 mapCreater(HashMap map) {
int value = 1;
for (int i = 0; i < 32; i++) {
map.put(value, i);
value <<= 1;
}
}
public static int[] randomArray(int maxKinds, int range, int k, int m) {
int ktimeNum = randomNumber(range);
// 真命天子出现的次数
int times = Math.random() < 0.5 ? k : ((int) (Math.random() * (m - 1)) + 1);
// 2
int numKinds = (int) (Math.random() * maxKinds) + 2;
// k * 1 + (numKinds - 1) * m
int[] arr = new int[times + (numKinds - 1) * m];
int index = 0;
for (; index < times; index++) {
arr[index] = ktimeNum;
}
numKinds--;
HashSet set = new HashSet<>();
set.add(ktimeNum);
while (numKinds != 0) {
int curNum = 0;
do {
curNum = randomNumber(range);
} while (set.contains(curNum));
set.add(curNum);
numKinds--;
for (int i = 0; i < m; i++) {
arr[index++] = curNum;
}
}
// arr 填好了
for (int i = 0; i < arr.length; i++) {
// i 位置的数,我想随机和j位置的数做交换
int j = (int) (Math.random() * arr.length);// 0 ~ N-1
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
return arr;
}
// [-range, +range]
public static int randomNumber(int range) {
return (int) (Math.random() * (range + 1)) - (int) (Math.random() * (range + 1));
}
public static void main(String[] args) {
int kinds = 5;
int range = 30;
int testTime = 100000;
int max = 9;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int a = (int) (Math.random() * max) + 1; // a 1 ~ 9
int b = (int) (Math.random() * max) + 1; // b 1 ~ 9
int k = Math.min(a, b);
int m = Math.max(a, b);
// k < m
if (k == m) {
m++;
}
int[] arr = randomArray(kinds, range, k, m);
int ans1 = test(arr, k, m);
int ans2 = onlyKTimes(arr, k, m);
if (ans1 != ans2) {
System.out.println(ans1);
System.out.println(ans2);
System.out.println("出错了!");
}
}
System.out.println("测试结束");
}
}