对于位运算的基础还有不熟悉的可以参考这篇:位运算操作符
表达式 | 含义 |
---|---|
(x & 1) == 1 | 奇数,等价于 x % 2 == 1 |
(x & 1) == 0 | 偶数,等价于 x % 2 == 0 |
x & (x - 1) | 把x最低位的二进制1给去掉 |
x & -x | 获取x最低位的1 |
性质 | 表达式 |
---|---|
交换律 | a ^ b ^ c <=> a ^ c ^ b |
任何数与 0 异或 为任何数 | 0 ^ n => n |
相同的数 异或 为0 | n ^ n => 0 |
功能 | 表达式 |
---|---|
x ∗ 2 n x * 2^n x∗2n | x << n |
x / 2 n x / 2^n x/2n | x >> n |
将 x 最右边的n位清零 | x & (~0 << n) |
获取 x 的第n位二进制值 | (x >> n) & 1(从低到高位算,从第0位开始) |
获取 x 的第n位的幂值 | x & (1 << n) |
仅将 x 的第n位 置为1 | x | (1 << n) |
仅将 x 第n位置为0 | x & (~(1 << n)) |
将 x 最高位至第n位(含)清零 | x & ((1 << n) - 1) |
将 x 的第n位至第0位(含)清零 | x & (~((1 << (n + 1)) - 1)) |
注意Java中没有同或,可以用异或取反
力扣136链接
描述:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素
示例:
输入: [4,1,2,1,2]
输出: 4
Solution:
此题在哈希表中也出现过,leetcode哈希表(哈希集合,哈希映射)
当时第一反应肯定是用HashSet的去重性
但是如果想要线性复杂度,就要想到用位运算。利用的就是异或的性质!
public int singleNumber(int[] nums) {
int res=0; // 0与任意数异或为任意数
// 对每个数进行相互异或,相同的全变成了0
for(int n:nums)
res^=n;
// 最后只剩下那个只出现一次的数字
return res;
}
力扣137链接
描述:
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
示例:
输入:nums = [0,1,0,1,0,1,99] 输出:99
Solution:
与上题很相似,但又觉得有些困难。当然,可以用哈希的方法(关于次数的题目大多和哈希有关)
还是从原始出发,依次计算最终答案的各个二进制位,一张图表明:
1.对于出现三次的数字,各位出现的次数都是3的倍数
2.统计所有数字的各二进制位中1的个数,并对3取余,结果位只出现一次的数字
3.对于数组中的每一个元素 x,我们使用位运算(x >> i) & 1
得到 x 的第 i 个二进制位(将第 i 位移到最低位与1相与,得到结果,因为1和0 和1 相与 为其自身)
4.将它们相加再对 3取余,得到的结果一定为 0 或 1,即为答案的第 i 个二进制位
代码:
public int singleNumber(int[] nums) {
// 初始化一个数组,用来记录结果的各个二进制位
int[] tmp = new int[32];
// 统计所有数字的各个二进制位的个数
for(int num:nums)
{
for(int i=0;i<32;i++)
{
// 将第 i 位移到最低位与1相与,得到结果
if(((num>>i)&1)==1)
tmp[i]++;
}
}
// ans 位最终结果
int ans=0;
for(int i=0;i<32;i++)
{
// 对于各个位不为0的
if ((tmp[i] % 3 & 1) == 1) {
// 将二进制转化位十进制,1<
ans+= (1 << i);
}
}
return ans;
}
稍微改进一下,不用辅助空间,直接处理:
public int singleNumber(int[] nums) {
int ans = 0;
for (int i = 0; i < 32; ++i) {
int total = 0;
for (int num: nums) {
total += ((num >> i) & 1);
}
if (total % 3 != 0) {
ans |= (1 << i);
}
}
return ans;
}
力扣260链接
描述:
给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
示例:
输入:nums = [1,2,1,3,2,5] 输出:[3,5]
Solution:
力扣268链接
描述:
给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。
示例:
输入:nums = [3,0,1] 输出:2
Solution:
此题与lc136 有异曲同工之妙,只需要多想一步
将这n个数与1~n 这n+1个数放在一起,共2n+1个数一起异或,最后的结果就是缺失的数
public int missingNumber(int[] nums) {
int res = 0;
for(int i=0;i<nums.length;i++)
{
res ^= i;
res ^= nums[i];
}
// 这里如果一开始的res初始化为数组长度,那最后输出就直接res
return res^nums.length;
}
描述:
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)
示例:
输入:00000000000000000000000000001011
输出:3
Solution:
利用最开始总结的技巧:n&(n-1) 去除二进制的低位1来计算二进制的1
public int hammingWeight(int n) {
int count=0;
while(n!=0)
{
n &= (n-1);
count++;
}
return count;
}
力扣231链接
描述:
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。
示例:
输入: n = 16 输出: true
Solution1:
和上一题有异曲同工之妙
因为2的幂次方的二进制只有1位是1,所以把最低位去掉之后就成了0。利用上面提到的 n & (n-1)
public boolean isPowerOfTwo(int n) {
return n>0 && ((n & (n-1))==0);
}
Solution2:
利用n & (-n) 可以获取最低位的1
public boolean isPowerOfTwo(int n) {
return n > 0 && (n & -n) == n;
}
力扣1356链接
描述:
给你一个整数数组 arr 。请你将数组中的元素按照其二进制表示中数字 1 的数目升序排序。
如果存在多个数字二进制中 1 的数目相同,则必须将它们按照数值大小升序排列。
请你返回排序后的数组。
示例:
输入:
Solution:
题号 | 难度 |
---|---|
easy |
链接:https://leetcode.cn/problems/power-of-two/solution/5chong-jie-fa-ni-ying-gai-bei-xia-de-wei-6x9m/