【算法一周目】位间流转,数字律动——洞察 C++ 位运算中的精妙与哲思

【算法一周目】位间流转,数字律动——洞察 C++ 位运算中的精妙与哲思

文章目录

常见位运算

  1. 判断一个数的二进制表示的第x位是0还是1
    • (n >> x) & 1
  2. 将一个数的二进制表示的第x位修改成1
    • n |= (1 << x)
  3. 将一个数的二进制表示的第x位修改成0
    • n &= ~ (1 << x)
  4. lowbit操作:提取一个数二进制表示中最右侧的1
    • n & -n
  5. 将一个数的二进制表示的最右侧的1修改成0
    • n & (n - 1)

1. 位1的个数

题目链接:191. 位1的个数

题目描述:

编写一个函数,输入一个无符号整数,返回该整数的二进制表示中 1 的个数。

示例

示例 1:

  • 输入:n = 00000000000000000000000000001011
  • 输出:3
  • 解释:输入的二进制串 00000000000000000000000000001011 中,共有 3 个 1

示例 2:

  • 输入:n = 00000000000000000000000010000000
  • 输出:1
  • 解释:输入的二进制串 00000000000000000000000010000000 中,共有 1 个 1

示例 3:

  • 输入:n = 11111111111111111111111111111101
  • 输出:31
  • 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 个 1

提示

  • 输入必须是一个无符号整数。

解题思路

对于这道题,我们可以将输入的 n n n 右移 32 32 32 次,逐位进行比较;也可以使用上面提到的消除一个数二进制最低位1的操作,统计多少次操作后 n n n 变为 0 0 0 ,得到的次数即为位1的个数。

代码实现

逐位比较

classSolution{public:inthammingWeight(int n){int ans =0;for(int i =0; i <32;++i){if((n >> i)&1) ans++;}return ans;}};

消除一个数二进制最低位1

classSolution{public:inthammingWeight(int n){int ans =0;while(n){ n &= n -1; ans++;}return ans;}};

2. 比特位计数

题目链接LCR 003. 比特位计数

题目描述

给定一个非负整数 n,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

示例 1:

  • 输入: n = 2
  • 输出: [0, 1, 1]
  • 解释: 0 --> 01 --> 12 --> 10

示例 2:

  • 输入: n = 5
  • 输出: [0, 1, 1, 2, 1, 2]
  • 解释:0 --> 01 --> 12 --> 103 --> 114 --> 1005 --> 101

说明:

  • 0 <= n <= 10^5

解题思路

这道题要每个数的位1的个数进行统计,很容易想到对每个数进行位运算,统计出每个数二进制表示的1的个数。

代码实现

classSolution{public:inthammingWeight(int n){int ans =0;for(int i =0; i <32;++i){if((n >> i)&1) ans++;}return ans;} vector<int>countBits(int n){ vector<int>ret(n +1);for(int i =1; i <= n;++i) ret[i]=hammingWeight(i);return ret;}};

这种解法的时间复杂度是 O ( 32 n ) O(32n) O(32n) ,但其实这道题还能用动态规划来做。

d p [ i ] dp[i] dp[i] :表示数字 i i i 二进制表示中1的个数

如果 i i i 是偶数,其二进制表示中最低位为 0 0 0 ,右移一位后二进制 1 1 1 的个数不变,则有 d p [ i ] = d p [ i > > 1 ] dp[i] = dp[i>>1] dp[i]=dp[i>>1]

如果 i i i 是计数,其二进制表示中最低位为 1 1 1 ,右移一位后二进制 1 1 1 的个数减少了一个,则有 d p [ i ] = d p [ i > > 1 ] + 1 dp[i] = dp[i>>1] + 1 dp[i]=dp[i>>1]+1

综上可得

  • 递推公式: d p [ i ] = d p [ i > > 1 ] + ( i & 1 ) dp[i] = dp[i >> 1] + (i\And1) dp[i]=dp[i>>1]+(i&1)

代码实现

classSolution{public: vector<int>countBits(int n){ vector<int>ret(n +1);for(int i =0; i <= n;++i) ret[i]= ret[i >>1]+(i &1);return ret;}};

动态规划的解法时间复杂度严格为 O ( n ) O(n) O(n) 。


3.汉明距离

题目链接:461. 汉明距离

题目描述:

两个整数的 汉明距离 是指这两个数字对应的二进制位不同的 positions 的个数。

给定两个整数 xy,计算它们之间的汉明距离。

示例 1:

  • 输入:2
  • 输出:2

示例 2:

  • 输入:x = 3, y = 1
  • 输出:1

提示:

  • 0 <= x, y <= 231 -1

解题思路

将输入的 x x x 和 y y y 右移,逐位比较,统计对应二进制位不同的位置的数目,当 x ∣ y x | y x∣y 为 0 0 0 时,说明两个数最高位 1 1 1 统计完,结束循环。

代码实现

classSolution{public:inthammingDistance(int x,int y){int ans =0;while(x | y){int a = x &1, b = y &1; ans += a ^ b; x >>=1; y >>=1;}return ans;}};

这道题还可以用 l o w b i t lowbit lowbit 来求解, l o w b i t lowbit lowbit 可以提取一个数二进制表示最右侧的 1 1 1 。

我们可以先将 x x x 和 y y y 异或起来,再统计异或结果中 1 1 1 的个数。

classSolution{public:intlowbit(int x){return x &-x;}inthammingDistance(int x,int y){int ret=0;for(int i = x ^ y; i >0; i -=lowbit(i)) ret++;return ret;}};

4. 只出现一次的数字

题目链接:136. 只出现一次的数字

题目描述:

给定一个整数数组 nums,其中元素出现的次数可以是 21,请你找出只出现一次的元素。

你必须实现一个线性时间复杂度的算法,并且不使用额外空间。

示例 1 :

  • 输入:nums = [2,2,1]
  • 输出:1

示例 2 :

  • 输入:nums = [4,1,2,1,2]
  • 输出:4

示例 3 :

  • 输入:nums = [1]
  • 输出:1

提示:

  • 1 <= nums.length <= 3 * 104
  • -3 * 104 <= nums[i] <= 3 * 104
  • 除了某个元素只出现一次以外,其余每个元素均出现两次。

解题思路

对这道题,我们可以使用哈希表来统计数字出现的次数,然后遍历一次哈希表找出出现次数为 2 2 2 的数字。

当然这道题更优的解法是使用异或运算,直接将所有的数异或起来就能得到出现次数为 1 1 1 的数字。

代码实现

classSolution{public:intsingleNumber(vector<int>& nums){int ret =0;for(auto num : nums) ret ^= num;return ret;}};

5. 只出现一次的数字 III

题目链接:260. 只出现一次的数字 III

题目描述:

给定一个整数数组 nums ,其中有两个元素只出现一次,其余元素都出现两次。请你找出那两个只出现一次的元素并返回。

你可以不使用额外空间,并且要求时间复杂度为 O(n)

示例 1:

  • 输入:nums = [1,2,1,3,2,5]
  • 输出:[3,5]
  • 解释:[5, 3] 也是有效的答案。

示例 2:

  • 输入:nums = [-1,0]
  • 输出:[-1,0]

示例 3:

  • 输入:nums = [0,1]
  • 输出:[1,0]

提示:

  • 2 <= nums.length <= 3 * 104
  • -231 <= nums[i] <= 231 - 1
  • 除两个只出现一次的整数外,nums 中的其他数字都出现两次

解题思路

这道题是只出现一次的数字的升级版,要求找到两个只出现一次的元素,我们用异或+分治的思路去解决。

  1. 将数组的所有数字异或起来,得到两个只出现一次的数字的异或值。
  2. 通过位运算得该异或值二进制表示最低位的 1 1 1 是第 i i i 位。
  3. 通过第 i i i 位上值的不同,将原数组划分,因为异或结果二进制表示位为 1 1 1 表示只出现一次的两个数字 a a a 和 b b b 该比特位的不同,我们只需要根据这个划分依据分别将对应的数字异或起来就能得到只出现一次的两个数字。

原理:异或运算,相同为 0 0 0 ,不同为 1 1 1 。

代码实现

classSolution{public: vector<int>singleNumber(vector<int>& nums){int x =0;//a ^ b = xfor(auto e : nums) x ^= e;//判断x第几位是1int i =0;for(i =0; i <32;++i)if((x >> i)&1)break;//根据第i位划分原数组int a =0, b =0;for(auto e : nums){if((e >> i)&1) a ^= e;else b ^= e;}return{a, b};}};

6. 只出现一次的数字 II

题目链接:137.只出现一次的数字 II

题目描述:

给定一个整数数组 nums ,其中每个元素出现三次,除了一个元素只出现一次。请你找出那个只出现一次的元素。

你必须实现一个线性时间复杂度的算法,并且不使用额外空间。

示例 1:

  • nums = [2,2,3,2]
  • 输出:3

示例 2:

  • 输入:nums = [0,1,0,1,0,1,99]
  • 输出:99

提示:

  • 1 <= nums.length <= 3 * 104
  • -231 <= nums[i] <= 231 - 1
  • nums 中,除某个元素仅出现一次外,其余每个元素都恰出现三次。

解题思路

题目要求我们找出只出现一次的元素,并且其他元素出现了 3 3 3 次。

既然这样,那么我们将每个数对应的比特位上的值加起来,将结果 % 3 \%3 %3 ,得到余数只有 0 0 0 和 1 1 1 (目标元素出现 1 1 1 次,其他元素出现 3 3 3 次),余数为 0 0 0 说明目标元素对应比特位上的值为 0 0 0,余数为 1 1 1 说明目标元素对应比特位上的值为 1 1 1。

这样我们只需要对数组所有元素的 32 32 32 个比特位进行同样的操作,就能一位一位的还原出只出现一次的元素。

代码实现

classSolution{public:intsingleNumber(vector<int>& nums){int x =0;for(int i =0; i <32;++i){int bitSum =0;for(auto e : nums) bitSum +=(e >> i)&1; x |=(bitSum %3)<< i;}return x;}};

7. 判定字符是否唯一

题目链接:面试题 01.01. 判定字符是否唯一

题目描述: 实现一个算法,确定一个字符串 s 的所有字符是否全都不同。

示例 1:

  • 输入:s = "leetcode"
  • 输出:false

示例 2:

  • 输入:s = "abc"
  • 输出:true

提示:

  • 0 <= len(s) <= 100
  • s[i] 仅包含小写字母
  • 如果不使用额外的数据结构,会很加分。

解题思路

一个整型变量有 32 32 32 位,而 s[i] 仅包含小写字母,小写字母共有 26 26 26 位,所有我们可以利用位图来充当哈希表,快速判断出判定字符是否唯一。

值得注意的是,如果字符串长度大于 26 26 26 ,那说明字符串里一定有重复的字符,直接返回 f a l s e false false 。

代码实现

classSolution{public:boolisUnique(string astr){//当字符串长度大于26,必定有重复字符if(astr.size()>26)returnfalse;//利用位图做哈希表int bitMap =0;for(auto ch : astr){int i = ch -'a';//判断字符是否已经存在if((bitMap >> i)&1)returnfalse;//加入哈希表 bitMap |=1<< i;}returntrue;}};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1),仅仅使用一个 int 的额外空间。

8. 丢失的数字

题目链接:268. 丢失的数字

题目描述:

给定⼀个包含 [0, n]n个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

示例 1:

  • 输入:nums = [3,0,1]
  • 输出:2
  • 解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 2:

  • 输入:nums = [0,1]
  • 输出:2
  • 解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 3:

  • 输入:nums = [9,6,4,2,3,5,7,0,1]
  • 输出:8
  • 解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。

示例 4:

  • 输入:nums = [0]
  • 输出:1
  • 解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。

提示:

  • n == nums.length
  • 1 <= n <= 104
  • 0 <= nums[i] <= n
  • nums 中的所有数字都独⼊无二

进阶:你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?


解题思路

对于这道题,我们可以用异或的解决。对不缺失数字的序列 [ 0 , n ] [0, n] [0,n] ,将其全部数异或起来,得到一个异或值,再用这个异或值与缺失数字的数组 n u m s nums nums 的每个元素异或,最后的得到的结果就是缺失的数字,因为重复的数字已经被消除掉了 ( x ⊕ x = 0 x \oplus x = 0 x⊕x=0),类比于136. 只出现一次的数字

再或者我们可以利用高斯公式快速求出不缺失数字的序列 [ 0 , n ] [0, n] [0,n] 的所有元素和,再减去缺失数字的数组 n u m s nums nums 的每个元素,最后得到的结果也是缺失的数字。

这道题利用哈希也能解决,不过空间复杂度是 O ( n ) O(n) O(n) ,相比之下,并不是最优解法。

代码实现

异或

classSolution{public:intmissingNumber(vector<int>& nums){int x =0;for(auto e : nums) x ^= e;for(int i =0; i <= nums.size();++i) x ^= i;return x;}};

高斯求和

classSolution{public:intmissingNumber(vector<int>& nums){int n = nums.size();//高斯公式求和int sum = n *(n +1)/2;for(auto e : nums) sum -= e;return sum;}};

哈希

classSolution{public:intmissingNumber(vector<int>& nums){int n = nums.size();int* hash =newint[n+1]{0};for(auto e : nums) hash[e]++;for(int i =0; i <= n;++i){if(hash[i]==0){delete[] hash;return i;}}return-1;}};

注意:C++不支持可变长数组 a r r [ n ] arr[n] arr[n] ,所以创建哈希表要使用动态开辟的方法,同时记得要进行初始化。


9. 两整数之和

题目链接:371. 两整数之和

题目描述: 给你两个整数 ab,不使用运算符 +-,计算并返回两整数之和。

示例 1:

  • 输入:a = 1, b = 2
  • 输出:3

示例 2:

  • 输入:a = 2, b = 3
  • 输出:5

提示:

  • -1000 <= a, b <= 1000

解题思路

异或的本质其实是无进位相加, a ⊕ b a \oplus b a⊕b 得到的是 a a a 和 b b b 无进位相加的结果。

而 a a a 和 b b b 的进位是 ( a & b ) < < 1 (a \And b) << 1 (a&b)<<1 ,将其左移是因为相加时是进位的 1 1 1 是加在当前位的下一位。

所以可以得出两整数之和 s u m = a + b ⇒ a ⊕ b + ( a & b ) < < 1 sum = a+b \Rightarrow a \oplus b + (a \And b) << 1 sum=a+b⇒a⊕b+(a&b)<<1

代码实现

迭代写法

classSolution{public:intgetSum(int a,int b){while(b){int x = a ^ b; b =(a & b)<<1; a = x;}return a;}};

递归写法

classSolution{public:intgetSum(int a,int b){return b ==0? a :getSum(a ^ b ,(a & b)<<1);}};

提多几句,实际上关于进位的处理实际上是有点问题的,因为 b b b 是带符号整数,左移操作时如果超出表示范围(左移相当于乘2),其结果是未定义的,所以进位我们最好用无符号整数类型 u n s i g n e d i n t unsigned int unsignedint 来存储,因为无符号整数超出表示范围会自动取模。


10. 只出现一次的数字 II

题目链接:面试题 17.19. 消失的两个数字

题目描述:

给定一个数组,包含从 1N 所有的整数,但其中缺了两个数字。你能在 O ( N ) O(N) O(N) 时间内只用 O ( 1 ) O(1) O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。

示例 1:

  • 输入:[1]
  • 输出:[2,3]

示例 2:

  • 输入:[2,3]
  • 输出:[1,4]

提示:

  • nums.length <= 30000

解题思路

这道题其实就是缝合了268. 丢失的数字260. 只出现一次的数字 III,我们还是使用异或+分治来解决问题。

  1. 异或 [ 1 , n ] [1, n] [1,n] 和数组 n u m s nums nums 的所有元素,得到异或值 x x x 。
  2. 异或结果二进制表示位为 1 1 1 表示缺失的两个数字 a a a 和 b b b 该比特位的不同,找出异或值二进制表示最低位的 1 1 1 是第 i i i 位。
  3. 通过第 i i i 位上值的不同,将 [ 1 , n ] [1, n] [1,n] 和数组 n u m s nums nums 的所有元素划分,我们只需要根据这个划分依据分别将对应的元素异或起来就能得到缺失的两个数字。

代码实现

classSolution{public: vector<int>missingTwo(vector<int>& nums){//1.求出缺失两个数的异或xint n = nums.size(), x =0;for(int i =1; i <= n+2;++i) x ^= i;for(auto e : nums) x ^= e;//2.找出a b二进制中不同的那位diffint diff =0;while(((x >> diff)&1)==0) diff++;//3.根据 diff 位的不同,将所有的数划分为两类并异或int a =0, b =0;for(auto e : nums)if((e >> diff)&1) a ^= e;else b ^= e;for(int j =1; j <= n+2;++j)if((j >> diff)&1) a ^= j;else b ^= j;return{a, b};}};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

Have a good day😏

See you next time, guys!😁✨🎞

请添加图片描述

Read more

告别996:GitHub Copilot将我的开发效率提升300%的实战记录

告别996:GitHub Copilot将我的开发效率提升300%的实战记录

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕AI这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * 告别996:GitHub Copilot将我的开发效率提升300%的实战记录 * 引言:从疲惫到高效 * 什么是GitHub Copilot?🤖 * 效率提升300%的核心场景 * 1. 快速生成样板代码 * 2. 自动编写单元测试 * 3. 智能调试与注释 * 集成Copilot到工作流 * 步骤1:设置合理的期望 * 步骤2:结合IDE使用 * 步骤3:代码审查与调整 * 高级用法:超越代码生成 * 数据库查询优化 * API接口设计 * 正则表达式助手 * 数据支撑:效率提升分析 * 避坑指南:常见问题与解决 * 1. 可能生成过时或不安全代码

By Ne0inhk
GTC2026前瞻(二)Agentic AI 与开源模型篇+(三)Physical AI 与机器人篇

GTC2026前瞻(二)Agentic AI 与开源模型篇+(三)Physical AI 与机器人篇

(二)Agentic AI 与开源模型篇 Agentic AI与开源模型:英伟达想定义的,不只是“更聪明的模型”,而是“能持续工作的数字劳动力” 如果说过去两年的大模型竞赛,核心问题还是“谁能生成更像人的答案”,那么到了 GTC 2026,问题已经明显变了。英伟达把 Agentic AI 直接列为大会四大核心主题之一,官方对这一主题的定义也很明确:重点不再是单轮问答,而是让 AI agent 能够推理、规划、检索并执行动作,最终把企业数据转化为可投入生产的“数字劳动力”。这说明,Agentic AI 在英伟达的语境里,已经不是一个前沿概念,而是下一阶段 AI 商业化的主战场。(NVIDIA) 一、GTC 2026真正的变化,是 AI 开始从“会回答”走向“会做事”

By Ne0inhk
GitHub CLI 安装指南

GitHub CLI 安装指南

GitHub CLI 是 GitHub 官方提供的命令行工具,可以帮助开发者方便地与 GitHub 平台进行交互,例如克隆仓库、提交代码、创建 Pull Request 等。 相比传统的 HTTPS 下载和操作,GitHub CLI 提供了以下显著的优势和特殊功能: GitHub CLI 的优势 1. 快速交互 GitHub 功能: * 不仅支持克隆仓库,还可以直接通过命令行创建 Issue、合并 Pull Request、管理 GitHub Actions 等操作。 * 节省了在 GitHub 网站和本地终端之间切换的时间。 2. 增强的身份验证支持: * 提供更安全的登录方式,支持 OAuth 和 SSH,不再需要手动输入用户名和密码。 * 支持 Personal

By Ne0inhk

CAM++智能家居:个性化语音助手的声纹唤醒机制

CAM++智能家居:个性化语音助手的声纹唤醒机制 1. 引言 随着智能家居设备的普及,用户对语音助手的安全性与个性化需求日益增长。传统语音唤醒系统往往依赖关键词检测(如“嘿 Siri”),但难以区分不同说话人,存在隐私泄露和误触发风险。为解决这一问题,基于声纹识别的个性化语音助手逐渐成为研究热点。 CAM++ 是由科哥开发的一套高性能说话人验证系统,其核心是 DAMO 团队提出的 CAM++(Context-Aware Masking++)模型,具备高精度、低延迟的特点,特别适用于资源受限的边缘设备。该系统不仅能判断两段语音是否来自同一说话人,还可提取 192 维的声纹特征向量(Embedding),为构建个性化的智能语音交互系统提供了坚实基础。 本文将深入解析 CAM++ 在智能家居场景下的应用逻辑,重点剖析其声纹唤醒机制的设计原理、工程实现路径以及优化策略,帮助开发者理解如何将其集成到实际产品中,打造真正“懂你”的语音助手。 2. 技术原理深度解析 2.1 声纹识别的本质与挑战 声纹识别(Speaker Verification)

By Ne0inhk