跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

位运算算法专题:字符唯一性与缺失数字问题解析

位运算算法实战:从字符唯一性到缺失数字的求解思路。通过五个经典算法题,深入讲解位运算在编程中的实际应用。涵盖利用位图判断字符唯一性、异或运算查找缺失数字、模拟加法器实现整数求和、比特位计数解决单次出现元素问题,以及组合技巧处理两个缺失数字的场景。重点在于理解位操作背后的逻辑而非死记硬背代码,适合希望提升底层思维能力的开发者参考。

微码行者发布于 2026/3/15更新于 2026/6/1732 浏览
位运算算法专题:字符唯一性与缺失数字问题解析

常见位运算总结

刷前必刷题单

在深入具体题目之前,先回顾几个经典的位运算技巧,这些是解决后续问题的基础。

1. 清除最右侧的 1 利用 n & (n - 1) 可以快速将整数二进制表示中最右侧的 1 变为 0。这在统计 1 的个数时非常高效。

class Solution {
public:
    int hammingWeight(int n) {
        int count = 0;
        while (n) {
            n &= (n - 1);
            count++;
        }
        return count;
    }
};

2. 汉明距离与异或 两个数异或后,结果为 1 的位数即为它们的汉明距离。结合上述技巧可快速计算。

class Solution {
public:
    int hammingDistance(int x, int y) {
        int val = x ^ y;
        int count = 0;
        while (val) {
            val &= (val - 1);
            count++;
        }
        return count;
    }
};

3. 只出现一次的数字 利用异或运算的自反性(a ^ a = 0, a ^ 0 = a),数组中成对出现的数字异或后会抵消,剩下的就是目标值。

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int result = 0;
        for (int num : nums) {
            result ^= num;
        }
        return result;
    }
};

核心要点

做题时不要死记硬背代码,重点在于理解位操作背后的逻辑。建议自己在纸上推导一遍二进制变化过程,这样遇到变体题也能举一反三。


033 判断字符是否唯一

题目描述: 给定一个字符串,判断其中是否包含重复字符。要求不使用额外的数据结构。

解法一:位图思想

既然输入只包含小写字母,我们可以用一个整数的 32 个比特位来代表 26 个字母的状态。如果某一位为 1,说明对应字符已出现过;若再次遇到该字符,则返回 false。

这里有个细节,如果字符串长度超过 26,根据鸽巢原理必然存在重复,可以直接提前返回。

class Solution {
public:
    bool isUnique(string astr) {
        if (astr.size() > 26) return false;
        
        int bitMap = 0;
        for (char ch : astr) {
            int i = ch - 'a';
            // 检查第 i 位是否为 1
            if ((bitMap >> i) & 1) return false;
            // 将第 i 位置为 1
            bitMap |= (1 << i);
        }
        return true;
    }
};

思路补充

这种用整数代替哈希表的方法在空间受限且数据范围明确时非常有效。注意位移操作的优先级,括号不能少。


034 丢失的数字

题目描述: 给定包含 0 到 n 中 n 个不同整数的数组,找出其中缺失的那个数。

解法:异或消去

数组原本应包含 0 到 n 的所有整数,现在缺了一个。如果我们把数组中的所有元素与 0 到 n 的所有数字进行异或,成对的数字会相互抵消(x ^ x = 0),最后剩下的就是缺失的那个数字。

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int ret = 0;
        // 异或数组中的元素
        for (int x : nums) ret ^= x;
        // 异或 0 到 n
        for (int i = 0; i <= nums.size(); i++) ret ^= i;
        return ret;
    }
};

思路补充

这种方法避免了求和可能导致的溢出问题,时间复杂度 O(n),空间复杂度 O(1)。关键在于理解异或的交换律和结合律。


035 两整数之和

题目描述: 不使用运算符 + 和 -,计算两个整数 a 和 b 的和。

解法:模拟加法器

计算机底层加法分为两步:无进位相加和计算进位。

  • 无进位和:通过异或 ^ 实现。
  • 进位:通过与 & 并左移 << 1 实现。

循环执行这两步,直到进位为 0,此时结果即为最终和。

class Solution {
public:
    int getSum(int a, int b) {
        while (b != 0) {
            // 无进位相加
            int sum = a ^ b;
            // 计算进位,需转为 unsigned 防止负数左移未定义行为
            unsigned int carry = (unsigned int)(a & b) << 1;
            a = sum;
            b = carry;
        }
        return a;
    }
};

思路补充

实际面试中如果遇到此类限制,通常考察的是对底层原理的理解。虽然直接写 return a + b 能通过测试,但无法体现技术深度。注意处理负数时的符号位扩展问题,使用 unsigned int 转换是一种安全的处理方式。


036 只出现一次的数字 II

题目描述: 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

解法:比特位计数

既然其他数字都出现了三次,那么对于任意一个比特位,所有数字在该位上的 1 的总数应该是 3 的倍数。如果加上目标数字后,某一位的 1 的总数不是 3 的倍数,说明目标数字在该位上是 1。

我们遍历 32 个比特位,统计每一位上 1 出现的次数,对 3 取余,即可还原出目标数字。

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

思路补充

这个方法比状态机解法更直观,容易理解。虽然时间复杂度是 O(32n),但在固定整数位宽下等同于 O(n)。注意处理负数时,C++ 中右移是算术移位,可能会保留符号位,但这里我们逐位构建结果,逻辑依然成立。


037 消失的两个数字

题目描述: 给定一个包含 0 到 n 中 n 个不同整数的数组,其中有两个数字消失了,找出这两个数字。

解法:组合策略

这道题可以看作是'丢失的数字'和'只出现一次的数字 III'的结合。

  1. 整体异或:将数组所有元素与 1 到 n+2 的所有数字异或。结果将是两个缺失数字的异或值(a ^ b)。
  2. 区分位:找到 a ^ b 结果中任意一个为 1 的位,这意味着 a 和 b 在这一位上不同。
  3. 分组异或:根据这一位将原数组和完整序列分成两组,分别异或。每组的结果即为一个缺失数字。
class Solution {
public:
    vector<int> missingTwo(vector<int>& nums) {
        int tmp = 0;
        // 异或数组元素
        for (int x : nums) tmp ^= x;
        // 异或 1 到 N
        for (int i = 1; i <= nums.size() + 2; i++) tmp ^= i;
        
        // 找到不同的那一位
        int diff = 0;
        while (!((tmp >> diff) & 1)) diff++;
        
        int a = 0, b = 0;
        // 分组异或
        for (int x : nums) {
            if ((x >> diff) & 1) b ^= x;
            else a ^= x;
        }
        for (int i = 1; i <= nums.size() + 2; i++) {
            if ((i >> diff) & 1) b ^= i;
            else a ^= i;
        }
        return {a, b};
    }
};

思路补充

核心在于利用异或的性质将大问题拆解。找到区分位是关键步骤,它保证了两个缺失数字会被分到不同的组,从而在各自的组内成为唯一的'落单者'。

目录

  1. 常见位运算总结
  2. 刷前必刷题单
  3. 核心要点
  4. 033 判断字符是否唯一
  5. 解法一:位图思想
  6. 思路补充
  7. 034 丢失的数字
  8. 解法:异或消去
  9. 思路补充
  10. 035 两整数之和
  11. 解法:模拟加法器
  12. 思路补充
  13. 036 只出现一次的数字 II
  14. 解法:比特位计数
  15. 思路补充
  16. 037 消失的两个数字
  17. 解法:组合策略
  18. 思路补充
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Visual Studio 2026 GitHub Copilot Agent 模式解析
  • HarmonyOS6 RcText 组件扩展能力详解
  • 自动泊车路径规划算法题解
  • Visual Studio 中 GitHub Copilot Agent 模式深度解析
  • 工业机器人离线编程(OLP)技术指南与优势分析
  • VS Code 主流 AI 编程助手前端体验对比:Copilot、通义灵码、iFlyCode 及 Trae
  • Fun-ASR WebUI 本地部署与使用指南
  • Visual C++ 运行库安装方案与常见 DLL 缺失问题修复
  • ComfyUI-Manager 插件安装与管理使用指南
  • VisualCppRedist AIO 项目介绍与使用指南
  • GitHub Copilot 模型对比与选型策略
  • Visual C++运行库修复指南:解决DLL缺失错误
  • Visual C++ 运行库安装与 DLL 缺失问题修复指南
  • Visual C++ 运行库修复指南:解决 Windows 系统组件依赖问题
  • C++ 插入排序算法原理与实现
  • Visual C++ 运行库缺失问题修复指南
  • AI 编程工具深度对比:Cursor、Copilot、Trae 与 Claude Code
  • 基于 Spring Boot 的学生成绩管理系统设计与实现
  • Visual C++ 运行库修复指南:解决 Windows 程序无法启动问题
  • 基于 Playwright 封装 Web 爬虫并隐藏自动化特征

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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