【每日算法】专题五_位运算

1. 常见的运算总结

1.1. 基础位运算

按位与(&):有 0 就是 0

按位或(|):有 1 就是 1

按位异或(^):相同为 0,相异为 1 / 无进位相加

按位取反(~):逐位翻转,0→1,1→0

左移(<<):整体左移,右侧补 0

右移(>>):整体右移,左侧补符号位(正补 0,负补 1)

1.2 给一个数n,确定它的二进制表示中的第 x 位是 0 还是 1

bash 复制代码
(n >> x) & 1

1.3 将一个数 n 的二进制表示的第 x 位修改成1

bash 复制代码
n |= (1 << x)

1.4 将一个数 n 的二进制表示的第 x 位修改成 0

bash 复制代码
n &= (~(1 << x))

1.5 位图的思想

位图的核心思想是用二进制位(bit)表示数据状态,通过每一位的 0/1 标记 "不存在 / 存在",将离散数据映射到连续位空间,利用位运算实现高效存储与操作。

1.6 提取一个数 n 二进制中最右侧的1

bash 复制代码
n & -n

1.7 干掉一个数 n 二进制表示中最右侧的 1

bash 复制代码
n & (n - 1)

1.8 异或(^)运算的运算律

bash 复制代码
a ^ 0 = a

a ^ a = 0

a ^ b ^ c = a ^ (b ^ c) 

2.例题

2.1 面试题 01.01. 判定字符是否唯一

面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)

cpp 复制代码
    bool isUnique(string astr) {
        // 运用位图的算法思想可以很轻松的解决这道题
        // 在整数的32bit位中一个bit位对应一个字符
        // 通过二进制位的 0/1 表示字符 "没出现过 / 出现过"

        if(astr.size() > 26) return false;// 当字符串长度大于26必然会出现重复的字符,直接返回false

        int bitMap = 0;
        for(auto ch : astr)
        {
            int i = ch - 'a';
            if((bitMap >> i) & 1) return false;// 字符对应的字节位置为1时说明字符重复出现
            bitMap |= (1 << i);// 给字符对应的字节位置标1
        }

        return true;
    }

2.2 丢失的数字

268. 丢失的数字 - 力扣(LeetCode)

cpp 复制代码
    int missingNumber(vector<int>& nums) {
        int bitMap = 0;
        for(int i = 0; i <= nums.size(); ++i)
            bitMap ^= i;

        for(auto i : nums)
            bitMap ^= i;

        return bitMap;
    }

2.3 两整数之和

371. 两整数之和 - 力扣(LeetCode)

cpp 复制代码
    int getSum(int a, int b) {
        // (a ^ b)可以进行无进位相加,(a & b) << 1可以把进位提出来。
        // 用进位与上一个无进位相加的结果进行无进位相加,继续把相加的进位提出来。
        // 以此循环知道进位为零为止。
        int ret = a ^ b;
        int Carry = (a & b) << 1;
        while (Carry)
        {
            int tmp = ret;
            ret = ret ^ Carry;
            Carry = (tmp & Carry) << 1;
        }
        return ret;
    }

2.4 只出现一次的数字 II

137. 只出现一次的数字 II - 力扣(LeetCode)

cpp 复制代码
    int singleNumber(vector<int>& nums) {

        // 将所有数的相同二进制位对应的1或0相加
        // 假设某个元素出现三次,且其对应的二进制位为1
        // 我们只需要将相加的结果模3就可以把出现三次的元素去掉,
        // 那么剩下就是仅出现一次的元素对应的二进制位的结果

        int ans = 0;

        for(int i = 0; i < 32; ++i)
        {
            int sum = 0;
            for(auto x : nums)
                if((x >> i) & 1) ++sum;

            sum %= 3;
            ans |= (sum << i);
        }
        return ans;
    }

2.5 只出现一次的数字 III

260. 只出现一次的数字 III

cpp 复制代码
    vector<int> singleNumber(vector<int>& nums) {
        // 假设nums中只出现一次的元素分别是x1 和 x2。
        // 将所有元素进行异或,最后剩下的只有x1^x2
        // 提取x1^x2的二进制中最右侧的1,设其为第 l 位,
        // 那么x1和x2中的某一个数的二进制表示的第 l 位为 0,
        // 另一个数的二进制表示的第 l 位为 1。
        // 这样我们可以根据元素的二进制表示的第 l 位为 0 还是 1 分成两类
        // x1 和 x2分别在这两个不同的类,最后将这两类数分别进行异或操作
        // 则可以分别得到 x1 和 x2

        // 把所有元素分为两类,一类包含x1 另一类包含x2
        int a = 0;
        for(int i = 0; i < nums.size(); ++i)
            a ^= nums[i];

        // INT_MIN取负会导致整数溢出
        int x = (a == INT_MIN ? a : a & -a);

        int x1 = 0, x2 = 0;
        for(int i = 0; i < nums.size(); ++i)
        {
            if(nums[i] & x) x1 ^= nums[i];
            else x2 ^= nums[i];
        }
        return {x1, x2};
    }

2.6 面试题 17.19. 消失的两个数字

面试题 17.19. 消失的两个数字 - 力扣(LeetCode)

根据题目2.2 和 2.5可以很轻松的解决这道题。

cpp 复制代码
    vector<int> missingTwo(vector<int>& nums) {
        int n = nums.size();
        
        int xorsum = 0;
        for(int i = 1; i <= n + 2; ++i) xorsum ^= i;

        for(auto num : nums) xorsum ^= num;
        int x = xorsum == INT_MIN ? xorsum : xorsum & (-xorsum);

        int x1 = 0, x2 = 0;
        for(int i = 1; i <= n + 2; ++i)
        {
            if(i & x) x1 ^= i;
            else x2 ^= i;

            if(i <= n)
            {
                if(nums[i - 1] & x) x1 ^= nums[i - 1];
                else x2 ^= nums[i - 1];
            }
        }

        return {x1, x2};
    }
相关推荐
猷咪11 小时前
C++基础
开发语言·c++
IT·小灰灰11 小时前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧11 小时前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q11 小时前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳011 小时前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾11 小时前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB11 小时前
WideCharToMultiByte与T2A
c++
2601_9498683611 小时前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
星火开发设计11 小时前
类型别名 typedef:让复杂类型更简洁
开发语言·c++·学习·算法·函数·知识
蒹葭玉树11 小时前
【C++上岸】C++常见面试题目--操作系统篇(第二十八期)
linux·c++·面试