本文已参与「新人创作礼」活动,一起开启掘金创作之路。
如何不用额外变量交换两个数
这个题目是要用异或来解决的。
int a = 10;
int b = 20;
a = a^b;
b = a^b;
a = a^b;
这个交换需要注意的是,如果是同一个地址上的元素的话,会被清零。
这边运用到的知识其实就是,相同的两个数异或会变成0,一个数和0异或还是原来的值。
一个数组中有一种数出现了奇数次,其他都出现了偶数次,打印那个奇数次的数
这道题也是利用异或来解决。
或许可以想到哈希表来解决,但是需要申请内存。
我们可以想想异或的知识点,相同的两个数异或会变成0,那么偶数次一起异或是不是就变成0了,然后奇数次的总会留下一个,那么这一个不就是答案了嘛。
int ret = 0;
for(int i = 0;i<arr.length;i++){
ret ^= arr[i];
}
System.out.println(ret);
我们创建一个变量,然后让这个变量循环和数组里面的数异或,最后得出的就是答案
如果有两个数是奇数次
这道题目是上面那个题目的进化版,那我们怎么做呢?
当然还是像上面那道题目一样,先定义一个变量,然后和数组里面的所有数异或。最后得到的数一定是这两个答案的异或,现在的目的就不一样了,我们现在要区分这两个数。
那我们怎么区分最后的答案呢?
我们想想异或的性质。相同为0,相异为1,那最后异或出来的数,有1的地方是不是他们不同的地方。
那就是说,有1的地方,就是一个数是0,一个数是1,那我们是不是把他们两个区分出来了,那我们就可以拿这个数的最右边的那个1来区分。
int ret = 0;
for(int i = 0;i<arr.length;i++){
ret ^= arr[i];
}
int rightOne = ret & (-ret); //这个就是提取int数最右边的那个1.
int ans1 = 0;
int ans2 = 0;
for(int i = 0;i<arr.length;i++){
if((arr[i] & rightOne) != 0){ //这个if就是为了区分两个不同的数的。
ans1 ^= arr[i];
}else{
ans2 ^= arr[i];
}
}
System.out.println(ans1);
System.out.println(ans2);
提取int数最右边的那个1
这个题目很简单,只要一个数&自己的相反数
假设 a = 12;
二进制:1100
-a = -12
二进制 0100
1100 & 0100 = 0100
一个数组中有一个数出现了K次,其他的数就出现了M次,求出现K次的数
这边还有一定的要求,M > 1 , K < M
这道题目还是很巧妙的,巧妙的地方在于,用数组来记录二进制的形式。我们可以申请一个长度为32的数组,然后里面的每一位都记录着这个数组里面所有数字的二进制1在某一位的个数。
然后我们对数组进行检索,如果有个位上的1不是M倍,那么就说明K次出现的数这个位置上是1.
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;
计算一个数有多少个1
这次我们要用到与这个符号。
我们知道1 & 0 = 0;
1 - 1 = 0;
10 - 1 = 01;
我们通过上面的二进制减法可以看到,减去1之后,我们最右边的那个1会变成0;
那我们循环减去然后和自己&,最后是不是会变成0;
那么代码就出来了
int num = 0;
int a = 15;
while(a != 0){
a = a & (a - 1); //这个就是代码的核心。
num++;
}
System.out.println(num);