目录
前言
在日常的刷题过程中, 大家可能经常会遇到一些位运算相关的题目, 本文将会向大家介绍一些常见的位运算算法, 以便于再遇到相关题目时, 能够有一些新的思路; 话不多说直接步入正题;
位运算常见的操作
下面列出的位运算常见的操作, 看看你能解决几个;
- 基础的位运算
要学习位运算相关的算法, 首先就要对基础的位运算操作熟悉;
基础的位操作:
- << 左移
- >> 右移
- ~ 按位取反
前三种较为简单, 不进行介绍;
- & 按位与 (有0就是0)
- | 按位或 (有1就是1)
- ^ 按位异或 (相同为0, 相异为1 或者 "无进位加法")
这些基础位操作要十分的熟悉, 才可以;
-
判断一个数n二进制第x位是0还是1
(n >> x) & 1
结果为0, 第x位就为0, 结果为1, 第x位就为1;
-
将一个数n二进制的第x位修改为1
n |= (1 << x)
-
将一个数n二进制的第x位修改为0
n = n &(~ (1 << x))
- 提取一个数二进制最低位的1
n & -n
- 干掉一个数二进制最低位的1
n & (n - 1)
位运算的优先级问题
解决办法非常简单, 添加括号;
异或运算的运算律
a ^ 0 = a;
a ^ a = 0;
a ^ b ^ c = a ^ (b ^ c);
如何得到两个异或在一起的数
在一些题目中将此遇到这样的场景, 需要将两个异或在一起的数分开; 这需要结合题目的场景来介绍;
力扣260题: https://leetcode.cn/problems/single-number-iii/
只出现一次的数字iii
给你一个整数数组 nums
,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。
示例 1:
输入:nums = [1,2,1,3,2,5] 输出:[3,5] 解释:[5, 3] 也是有效的答案。
示例 2:
输入:nums = [-1,0] 输出:[-1,0]
示例 3:
输入:nums = [0,1] 输出:[1,0]
这道题目就这涉及到将两个异或的数字区分开的问题;
思路: 先将所有的数字异或在一起, 结果就是两个只出现一次的数字异或的结果; 我们只需将两个异或在一起的数字分开即可;
如何将两个异或在一起的数字分开?
两个不同的数, 异或在一起的结果一定不为0;
那么异或的结果二进制位中就至少有1个1;
找到一个为1的比特位(最低位为1的那一位) 我们设为diff位;
使用位运算 x & -x 取出 x 的二进制表示中最低位那个 1
根据异或的特性, 将数组nums中所有的数字分为两类;
- 第diff位为1
- 第diff位为0
在数组 nums 中出现两次的元素,该元素的两次出现会被包含在同一类中;
在数组 nums 中只出现了一次的元素,它们会被包含在不同类中;
为1的异或到a, 为0的异或到b; 最终的a, b就是要的结果;
vector<int> singleNumber(vector<int>& nums)
{
int x = 0; // 最终两个不同的数异或在一起的结果
for(int n : nums)
{
x ^= n;
}
int a = 0, b = 0;
int t = (x==INT_MIN ? x : x & (-x)); // 找到最低位的1
for(int n : nums)
{
if(t & n) a ^= n; // 根据diff位区分两类数
else b ^= n;
}
return {a, b};
}
有了这道题大家可以去练练这一道题:
消失的两数:https://leetcode.cn/problems/missing-two-lcci/
思路也是一样的:
vector<int> missingTwo(vector<int>& nums)
{
int t = 0;
for(auto n:nums) t ^= n;
for(int i = 1; i <= nums.size() + 2; i++) t ^= i;
int a = 0, b = 0;
int x = (t & -t);
for(auto n:nums)
{
if(x & n) a ^= n;
else b ^= n;
}
for(int i = 1; i <= nums.size() + 2; i++)
{
if(x & i) a ^= i;
else b ^= i;
}
return {a, b};
}
总结
以上便是本文的全部内容, 希望可以在刷题时, 能为你提供新的思路, 最后, 感谢阅读!