[LC优选算法#11] 位运算 | 判断字符是否唯一 | 丢失的数字

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 = a
  • a ^ a = 0
  • 满足交换律、结合律

位运算符优先级(从高到低)

优先级 运算符 说明
1 ~ 取反(单目运算符)
2 << >> 左移、右移
3 & 按位与
4 ^ 按位异或
5 ` `

建议 :不确定优先级时一律加括号 (),可读性更好,也不容易出错。

2. 例题分析

2.1 判断字符是否唯⼀

判断字符是否唯⼀

解题思路:

这题我们可以创建一个哈希表来解决,但是空间复杂度有点高,不是最优的解法。

又因为字符串中仅包含26个小写字母,我们就可以借助比特位来模拟哈希表的存储方式。

  1. 位运算 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中寻找缺失数字的题目,但那是以数组有序为前提的。这道题中的数组无序,如果同样使用二分算法,就需要先排序再查找,时间复杂度不是最优解。

  1. 哈希表 O(N):先存后查找。
  2. 求和相减 O(N):0~n求和后逐一减去数组中的元素。
  3. 位运算 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;
    }
};

// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~