跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C++算法

位运算算法基础与经典例题解析

位运算直接操作整数二进制位,效率高。文章总结基础运算符及优先级,讲解位图思想、提取最右侧 1 等技巧。通过判定字符唯一、丢失数字、两整数之和、只出现一次数字 II 及消失的两个数字等经典题目,展示异或抵消、高斯求和、二分查找及位统计等算法应用,提供 C++ 代码实现与复杂度分析。

道系青年发布于 2026/3/16更新于 2026/4/275 浏览
位运算算法基础与经典例题解析

位运算算法基础与经典例题解析

本篇是优选算法之位运算算法,这是一种直接对整数在内存中的二进制位进行操作的运算,它的运算效率高,在快速幂算法、汉明重量、找出数组中唯一出现一次的数字、不使用额外变量交换两个数等场景中应用广泛。

1. 常见位运算总结

1.1 基础位运算符号

这六个位运算符是实现位运算算法的重要运算符,在 C 语言阶段有详细的介绍过。

位运算符号

记法如图所示,强调一下什么是无进位相加?

异或运算的规则决定了它天然契合无进位相加的概念。异或运算在比较两个二进制位时,0 ^ 0 = 0,0 ^ 1 = 1,1 ^ 0 = 1,1 ^ 1 = 0。只是单纯对比两个数在每一位上的值,将不同的视为 1,相同的视为 0,不涉及向高位进位。

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

判断第 x 位

约定二进制位从右到左,为最低位到最高位,定义为从 0 到 31,为的就是对应右移 x 位刚好对应第 x 位。所以将要比较的数 n 的第 x 位右移 x 位,与 1 按位与 &,如果是 0,第 x 位为 0;如果是 1,第 x 位是 1。

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

修改为 1

要修改数 n 第 x 位为 1 就不能破坏原来的数,所以将 1 移动 x 位,与 1 按位或 |,只修改了我们想要修改的那一位。

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

修改为 0

要修改数 n 第 x 位为 0 就不能破坏原来的数,所以将 1 移动 x 位,取反 ~ 为 0,与 1 按位与 &,只修改了我们想要修改的那一位。

1.5 位图的思想

位图思想

位图其实和哈希表相似,哈希表是额外开辟一个空间,计算数据出现频次,而位图则是把数据存在数据类型一个个字节里,这就省去了多开一个空间,然后利用上述的方法修改为 1 或 0,统计数是否出现过。

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

提取最右侧 1

将数 n 取反后 +1 得到相反数 -n,然后两数按位与 & 得到最右侧的 1。即最右侧的 1 及其右边都不变,左边的数都变成 0。

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

干掉最右侧 1

将数 n 减 1,然后两数按位与 & 干掉最右侧的 1。即最右侧的 1 及其右边都变成 0,左边的数都不变。

1.8 位运算的优先级

通常优先级为:~ > & > ^ > | 但是记起来太麻烦了,干脆直接加括号更好。

1.9 异或运算符 ^ 的运算律

异或运算律

这是异或运算符^常用的运算律,在题目中经常用。

2. 判定字符是否唯一

题目描述:

题目描述

示例:

示例

参考链接:判定字符是否唯一

题解:

通常统计多数的字母出现次数一般想到的是哈希表,时间空间复杂度都为 O(n),这就有人问了,有没有既简单又强势的方法能够解决?有的兄弟有的,这么强势的方法有九个,因为本题只涉及 26 个小写英文字母,所以可以用减少开辟空间的位图。

位图解法

如上述介绍位图一样,用 1 和 0 表示字母是否出现过,如果为 1,就返回 false;如果是 0,就添加 1 到指定位数上,遍历完字符串后返回 true。

细节问题:

鸽巢原理

利用鸽巢原理,如果字符串长度大于 26,那么必定有字母是重复的,所以大于 26 直接返回 false。

代码实现:

#include <iostream>
#include <string>
using namespace std;

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;
            }
            bitMap |= 1 << i;
        }
        return true;
    }
};

3. 丢失的数字

题目描述:

题目描述

示例:

示例

参考链接:丢失的数字

题解:

该题一共有四种方法解决。

哈希表

哈希表

把 0 ~ n 中出现的所有数字都放进哈希表里,然后遍历一遍哈希表,如果某一格内对应的 0,那么该数就是缺失的数字。

高斯求和

高斯求和

利用简单的求和公式求出 0 ~ n 所有数的和,然后减去缺失数字的数组,剩下的数就是题意所求。

二分查找

二分查找

先对数组进行排序,在连续数组的前提下,缺失数字的位置开始下标与实际值不同,很明显二段性立马就出来了,如果在右区间,那么 mid 会有等于缺失值的实际位置索引,即 right = mid;如果在左区间,mid 及其前面的值都不可能是缺失值的实际位置索引,即 left = mid + 1。

位运算

位运算

根据异或运算^的运算律,相同的两个数异或会抵消成 0,所以显而易见,把缺失数字的数组和完整的数组异或,剩下的就是缺失的数字。

代码实现:

#include <iostream>
#include <vector>
using namespace std;

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;
    }
};

4. 两整数之和

题目描述:

题目描述

示例:

示例

参考链接:两整数之和

题解:

很显然本题是一道为了笔试而出题的题,通常是不会要我们这样去计算的,如果是在笔试环节时,可以投机取巧,直接 return a+b 通过测试用例,一般面试官也不会去看你的代码。

投机取巧

言归正传,该题主要使用异或运算 + 无进位相加解决。

我们知道无进位相加就是只相加不进位,所以我们只要解决了进位问题,那么问题就迎刃而解了,那么进位也只会在两个位都为 1 的情况下才会进位,观察发现进位的操作就是按位与&,注意要进位的是下一位,所以要把按位与&完的结果右移一位。两个结果不断重复上述操作,直到进位为 0,就是相加的最终结果。

细节问题:

注意进位的数有可能因为一直右移导致为 -1,只要强转为无符号整数就行。

代码实现:

#include <iostream>
#include <vector>
using namespace std;

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 = carry;
        }
        return a;
    }
};

5. 只出现一次的数字Ⅱ

题目描述:

题目描述

示例:

示例

参考链接:只出现一次的数字Ⅱ

题解:

本题的解法是一种通用解法,以后遇到类似的题目思路是一样的,但是前提是要见过这种解法,这种思路十分的巧妙。

解题思路

因为除去单独的数,每个数的一位必定出现三次,也就是三的倍数。

位统计

所以每个数的指定位数之和必定为如图四种情况的一种,对加和总数求余数发现剩下的数就是那个单独的数的指定位数,很好,如此一来就发现了规律,如此循环往复,把每一位存入位图就能求出只出现一次的数。

细节问题:

实际上本题还能改成出现 n 次,只要把求余数时改成除 n 就行,其余的算法思路是一样的。

代码实现:

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (int i = 0; i < 32; ++i) {
            int sum = 0;
            for (auto x : nums) {
                if (((x >> i) & 1) == 1) {
                    sum++;
                }
            }
            sum %= 3;
            if (sum == 1) {
                ret |= 1 << i;
            }
        }
        return ret;
    }
};

6. 消失的两个数字

题目描述:

题目描述

示例:

示例

参考链接:消失的两个数字

题解:

本题是丢失的数字的延伸扩展,难度可以说是上升不少,掌握了这题以后无论是丢失了几个数字都可以用相同的思路来做。

第一步:

显而易见,首先利用异或的特性把 nums 和完整数组异或,得到缺失的两个数的异或结果,即 a ^ b。

异或结果

第二步:

接下来是最关键的一步,我们要知道除了 a、b 以外的数在异或时是偶数个,所以能够相互抵消,已知 a ^ b = 1,所以两个数异或后为 1 的那一位,表示在这一位上两个数必然不同,一个数为 1,另一个数为 0。那么我们就可以根据这个差异,异或后为 1 有很多位,我们选取最右侧的 1 来分组方便计算。

分组策略

基于此,我们把最右侧的 1 这一位定为 diff,先把 nums 进行分类,如果 nums 在 diff 位是 1,那么就和 a 异或^;如果 nums 在 diff 位是 0,那么就和 b 异或^。那么我们现在就是把有差异的那一位和 nums 异或并分类了,所以我们还要和一个完整的数组分类异或,抵消掉别的数,因为相同异或为 0,不同异或为 1,由于前面的分类,除了丢失的数,其他的数都抵消了,丢失的数也在异或的过程中把剩余位数补上了。

代码实现:

#include <iostream>
#include <vector>
using namespace std;

class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int ret = 0;
        for (auto x : nums) {
            ret ^= x;
        }
        for (int i = 1; i <= nums.size() + 2; ++i) {
            ret ^= i;
        }
        int diff = 0;
        while (1) {
            if (((ret >> diff) & 1) == 1) {
                break;
            } else {
                diff++;
            }
        }
        int a = 0, b = 0;
        for (auto 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};
    }
};

目录

  1. 位运算算法基础与经典例题解析
  2. 1. 常见位运算总结
  3. 1.1 基础位运算符号
  4. 1.2 给一个数 n,确定它的二进制表示中的第 x 位是 0 还是 1
  5. 1.3 将一个数 n 的二进制表示的第 x 位修改成 1
  6. 1.4 将一个数 n 的二进制表示的第 x 位修改成 0
  7. 1.5 位图的思想
  8. 1.6 提取一个 n 二进制表示中最右侧的 1
  9. 1.7 干掉一个数 n 二进制表示中最右侧的 1
  10. 1.8 位运算的优先级
  11. 1.9 异或运算符 ^ 的运算律
  12. 2. 判定字符是否唯一
  13. 3. 丢失的数字
  14. 4. 两整数之和
  15. 5. 只出现一次的数字Ⅱ
  16. 6. 消失的两个数字
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 产品经理入门指南:《AI 赋能》书籍推荐与学习路径
  • VSCode GitHub Copilot 安装与使用指南
  • 微软 Edge Webview2 v144 升级导致 SAP GUI 白屏故障及解决方案
  • PUSHI G1 AI+AR 眼镜开源方案与 18 个全场景应用解析
  • C++ 字符串类基础与算法实战
  • 火山引擎 LAS AI 数据湖助力千万小时级视频数据处理实践
  • FPGA 工程师岗位方向详解:逻辑、算法、底层及系统应用
  • Coze 智能体开发:插件、知识库与数据库全解析
  • 通过官方 API 搭建 QQ 群聊机器人
  • 前端表单验证策略与最佳实践
  • DeepSeek 中冷启动数据与多阶段训练的作用
  • OpenClaw 飞书 AI 机器人搭建指南
  • AI Agent 架构:基础组成模块深度解析
  • Python 基础命令与语法详解
  • Java Web 教师个人成果管理系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0
  • 基于 ESP32 的 ADS1115 多通道数据采集与 Web 监控系统
  • 基于 OpenClaw 与 Claude 的自动化写作工作流搭建
  • 无人机多源融合定位:GPS/北斗标定、抗干扰与精度提升
  • Vue 3 重构 Dify 聊天前端:项目搭建与基础架构
  • DeepSeek 中冷启动数据与多阶段训练的作用

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online