1. 位运算符和常见操作
位运算是直接对整数二进制位进行操作的高效运算方式:
| 符号 | 描述 | 运算规则 |
|---|---|---|
& |
与 | 两个位都为1时,结果才为1 |
| ` | ` | 或 |
^ |
异或 | 两个位相同为0,相异为1(无进位相加) |
~ |
取反 | 0变1,1变0 |
<< |
左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> |
右移 | 各二进位全部右移若干位,高位补0或符号位补齐 |
常用操作:
| 操作 | 公式 |
|---|---|
| 判断第 x 位(0或1) | (n >> x) & 1 |
| 修改第 x 位置为 1 | `n |
| 修改第 x 位置为 0 | n & ~(1 << x) |
| 提取最右侧的 1 | n & -n |
| 去掉最右侧的 1 | n & (n - 1) |
| 判断奇偶 | n & 1 |
| 乘以 2 | n << 1 |
| 除以 2 | n >> 1 |
异或运算规律
a ^ 0 = aa ^ a = 0- 满足交换律、结合律
位运算符优先级(从高到低)
| 优先级 | 运算符 | 说明 |
|---|---|---|
| 1 | ~ |
取反(单目运算符) |
| 2 | << >> |
左移、右移 |
| 3 | & |
按位与 |
| 4 | ^ |
按位异或 |
| 5 | ` | ` |
建议 :不确定优先级时一律加括号
(),可读性更好,也不容易出错。
2. 例题分析
2.1 判断字符是否唯⼀
解题思路:
这题我们可以创建一个哈希表来解决,但是空间复杂度有点高,不是最优的解法。
又因为字符串中仅包含26个小写字母,我们就可以借助比特位来模拟哈希表的存储方式。
- 位运算
O(N):利用位图 的思想,每⼀个**⽐特位** 代表⼀个字符 ,⼀个int类型的变量的32位⾜够表⽰所有的⼩写字⺟。⽐特位⾥⾯如果是 0 ,表⽰这个字符没有出现过。⽐特位⾥⾯的值是1,表⽰该字符出现过。
优化点 :鸽巢原理,当字符串长度大于26时,说明一定会重复,此时返回false。
cpp
class Solution {
public:
bool isUnique(string astr)
{
if(astr.size() > 26) //鸽巢原理
{
return false;
}
int hash = 0;
for(auto ch : astr)
{
int pos = ch - 'a';
if(((hash >> pos) & 1) == 0)
{
hash |= (1 << pos);
}
else
{
return false;
}
}
return true;
}
};
2.2 丢失的数字
解题思路:
在之前二分算法的章节中,我们也遇到过需要在0~n中寻找缺失数字的题目,但那是以数组有序为前提的。这道题中的数组无序,如果同样使用二分算法,就需要先排序再查找,时间复杂度不是最优解。
- 哈希表
O(N):先存后查找。 - 求和相减
O(N):0~n求和后逐一减去数组中的元素。 - 位运算
O(N):根据异或 的特点(相同为0),我们可以将数组中所有的数以及0~n中的数全部异或在一起,重复出现的数会抵消为0,最后落单的数即为数组中缺失的元素了。
cpp
class Solution {
public:
int missingNumber(vector<int>& nums)
{
int ret = 0;
int n = nums.size();
for(int i=0; i<n; i++)
{
ret ^= nums[i];
ret ^= i + 1;
}
return ret;
}
};
// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~