文章目录
- 预备知识
- 判断字符是否唯⼀
- 丢失的数字
- 两整数之和
- 只出现⼀次的数字
- 只出现⼀次的数字II
- 消失的两个数字
预备知识
给一个数n,确定它的二进制表示中的第x位是0还是1
(n>>(x-1)) & 1 == 1
true是1,false是0
将一个数n的二进制表示的第x位修改成1
n |= (1<<(x-1))
将一个数n的二进制表示的第x位修改成0
n &= (~(1<<(x-1)))
提取一个数n二进制表示中最右侧的1
n & -n
-n 相比 n 是将最右侧的1左边的区域全部取反
干掉一个数n二进制表示中最右侧的1
n = n & (n-1)
n-1是将最右侧的1右边的区域(包含1)全部取反
异或(^)运算的运算律
1.两个相同的数异或的结果一定为0。(a^a== 0)
2.任何数与0异或都等于它自己。(a^0== a)
3.异或满足交换律;
判断字符是否唯⼀
算法思路(位图的思想):
利用「位图」的思想,每⼀个「比特位」代表⼀个字符,⼀个 int 类型的变量有 32 位足够表示所有的小写字⺟。比特位里面如果是 0 ,表示这个字符没有出现过。比特位里面的值是 1 表示该字符出现过。
那么我们就可以用⼀个「整数」来充当「哈希表」。
class Solution {public boolean isUnique(String astr) {//利用鸽巢原理来做优化if(astr.length() > 26) return false;int bitMap = 0;for(int i = 0; i < astr.length(); i++) {int x = astr.charAt(i) - 'a';//先判断字符是否在位图中if(((bitMap >> x) & 1) == 1) return false;//把当前字符加入到位图中bitMap |= 1 << x;}return true;}
}
丢失的数字
算法思路:
可以用哈希表和高斯求和, 我们这里用位运算
如果我们把数组中的数,以及 [0, n] 中的数全部「异或」在⼀起,那么根据「异或」运算的两个相同的数异或的结果一定为0,最终的异或结果就是缺失的数
class Solution {public int missingNumber(int[] nums) {int ret = 0;for(int i = 0; i < nums.length; i++) ret ^= nums[i];for(int i = 0; i <= nums.length; i++) ret ^= i;return ret;}
}
两整数之和
算法思路:
- 异或 ^ 运算本质是「⽆进位加法」;
- 按位与 & 操作能够得到「进位」;
- 然后⼀直循环,直到「进位」变成 0 为⽌。
class Solution {public int getSum(int a, int b) {while(b != 0) {int tmp = (a & b) << 1;//需要进位a ^= b;//无需进位b = tmp;}return a;}
}
递归:
class Solution {public int getSum(int a, int b) {if(b == 0) return a;return getSum(a^b, (a&b)<<1);}
}
只出现⼀次的数字
class Solution {public int singleNumber(int[] nums) {int ret = 0;for(int x: nums) ret ^= x;return ret;}
}
只出现⼀次的数字II
算法思路(比特位计数):
设要找的数为 ret 。由于整个数组中,需要找的元素只出现了「⼀次」,其余的数都出现了「三次」,因此我们可以根据所有数的「某⼀个比特位」的总和 %3 的结果,快速定位到 ret 那位「比特位」的值是0 还是 1 。
这样,我们通过 ret 每⼀个比特位上的值,就可以将 ret 还原出来。
class Solution {public int singleNumber(int[] nums) {int ret = 0;for(int i = 0; i < 32; i++) { //依次修改ret中的每一个比特位int sum = 0;for(int x: nums) //统计 nums 中所有的数的第 i 位的和if(((x >> i) & 1) == 1)sum++;sum %= 3;if(sum == 1) ret |= 1 << i;}return ret;}
}
消失的两个数字
算法思路:
只要是不同的数,异或后肯定有1, 1的那个比特位不相同,所以我们可以把它们分成两组
过程:
1.全部异或在一起
2.找出两个数不同的比特位
3.分组异或
class Solution {public int[] missingTwo(int[] nums) {//1.先把所有数异或在一起int tmp = 0;for(int x: nums) tmp ^= x;for(int i = 1; i <= nums.length + 2; i++) tmp ^= i;//2.找出两个数不同的比特位int diff = 0;while(true) {if(((tmp >> diff) & 1) == 1) break;else diff++;}//3.将数组的数和[1, n + 2] 区间内所有数按照diff位不同,分两类异或int[] ret = new int[2];for(int x: nums) {if(((x >> diff) & 1) == 1) ret[1] ^= x;else ret[0] ^= x;}for(int i = 1; i <= nums.length + 2; i++) {if(((i >> diff) & 1) == 1) ret[1] ^= i;else ret[0] ^= i;}return ret;}
}