算法沉淀五位运算
算法沉淀五:位运算
位运算内容总结
1.基础位运算
« » ~
&:有0就是0
| :有1就是1
^:相同为0,相异为1 / 无进位相加
2.给一个数n,确定它的二进制表示中的第x位是0还是1
&上一个1,要么让第x位右移,要么让1左移,一般用右移x位, (n»x)&1
3.将一个数n的二进制表示的第x位修改成1
n |= (1«x) 等价于 n = n | (1 « x)
4.将一个数n的二进制表示的第x位修改为0
n &= ~(1«x) 1左移之后按位取反
5.位图的思想
2,3,4其实就是一类问题,本质就是一个哈希表,很多情况下就是一个数组。位图就是用一个整形变量的二进制比特位来记录信息。
6.提取一个数(n)二进制表示中最右侧的1 (lowbit)
n & -n
如何表示-n:n的按位取反再加1,本质就是将最右侧的1的左边区域全部变成相反。
7.去掉一个数(n)二进制表示中最右侧的1
n & (n - 1)
n-1:将最右侧的1的右边的区域(包含1)全部取反
8.位运算的优先级
能加括号就加括号
9.异或(^)运算的运算律
a ^ 0 = a
a ^ a = 0
a ^ b ^ c = a ^ (b ^ c)
面试题01.01.判断字符是否唯一【简单】
实现一个算法,确定一个字符串
s
的所有字符是否全都不同。示例 1:输入:
s
= “leetcode” 输出: false示例 2:输入:
s
= “abc” 输出: true限制:
0 <= len(s) <= 100
s[i]
仅包含小写字母- 如果 你不使用额外的数据结构,会很加分 。
解法一:哈希表
解法二:位图
优化点:鸽巢原理(抽屉原理)
如果字符串长度>26个字母,必定有一个字符是重复的。
class Solution {
public:
bool isUnique(string astr) {
if(astr.size() > 26) return false;//鸽巢原理做优化
int bitMap = 0;//设置位图
for (auto ch : astr)
{
int i = ch - 'a';//取得比特位之差
if (((bitMap >> i) & 1 == 1)) return false;
else bitMap |=(1<<i);//此时位图中没有这个字符,所以将i位的位图设置位1
}
return true;
}
};
268.消失的数字【简单】
给定一个包含
[0, n]
中n
个数的数组nums
,找出[0, n]
这个范围内没有出现在数组中的那个数。示例 1:
输入: nums = [3,0,1]
输出: 2
解释:
n = 3
,因为有 3 个数字,所以所有的数字都在范围[0,3]
内。2 是丢失的数字,因为它没有出现在nums
中。示例 2:
输入: nums = [0,1]
输出: 2
解释:
n = 2
,因为有 2 个数字,所以所有的数字都在范围[0,2]
内。2 是丢失的数字,因为它没有出现在nums
中。示例 3:
输入: nums = [9,6,4,2,3,5,7,0,1]
输出: 8
解释:
n = 9
,因为有 9 个数字,所以所有的数字都在范围[0,9]
内。8 是丢失的数字,因为它没有出现在nums
中。
解法一:哈希表
解法二:高斯求和公式
解法三:位运算(异或):补齐数组再异或
class Solution {
public:
int missingNumber(vector<int>& nums) {
int ret = 0;
for (auto x : nums) ret ^= x;
for (int i = 0; i <= nums.size(); i++) ret ^= i;
return ret;
}
};
137.只出现一次的数字Ⅱ
给你一个整数数组
nums
,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。 请你找出并返回那个只出现了一次的元素。你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。
示例 1:输入: nums = [2,2,3,2] 输出: 3
示例 2:输入: nums = [0,1,0,1,0,1,99] 输出: 99
思路:
- 逐位处理 :遍历整数的每一位(共32位,因为
int
类型占32位)。- 统计每位1的次数 :对于每一位,统计数组中所有数字在该位上1出现的总次数。
- 取模确定结果位 :由于其他数字出现三次,该位的总次数对3取余即为唯一数字在该位的值(0或1)。
- 组合结果 :将各二进制位的值组合,得到最终结果。
int singleNumber(vector<int>& nums) {
int res = 0;
for (int i = 0; i < 32; ++i) { // 遍历每一位(0到31)
int cnt = 0;
for (auto x : nums) { // 统计所有数字在第i位的1的总数
cnt += (x >> i) & 1; // 取出x的第i位,累加到cnt
}
res |= (cnt % 3) << i; // 取余得到结果位,合并到res
}
return res;
}
371.两整数之和
给你两个整数
a
和b
, 不使用 运算符+
和-
,计算并返回两整数之和。示例 1:输入: a = 1, b = 2 输出: 3
示例 2:输入: a = 2, b = 3 输出: 5
class Solution {
public:
int getSum(int a, int b) {
while(b != 0)
{
int x = a ^ b;
unsigned int carry = (unsigned int)(a & b) << 1;
a = x;
b = (int)carry;
}
return a;
}
};
面试题17.19 消失的两个数字
给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:输入:
[1]
输出: [2,3]示例 2:输入:
[2,3]
输出: [1,4]
class Solution {
public:
vector<int> missingTwo(vector<int>& nums) {
//1.将所有的数异或在一起
int tmp = 0;
for (auto x : nums) tmp ^= x;
for (int i = 0; i <= nums.size() + 2; i++) tmp ^= i;
//2.找出a,b中比特位不同的那一位
int diff = 0;
while(1)
{
if (((tmp >> diff) & 1) == 1) break;
else diff++;
}
//3.根据diff位的不同,将所有的数划分为两类来异或
int a = 0, b = 0;
for (int x : nums)
if (((x >> diff) & 1) == 1) b ^= x;
else a ^= x;
for (int i = 1; i <= nums.size() + 2; i++)
if (((i >> diff) & 1) == 1) b ^= i;
else a ^= i;
return {a, b};
}
};