位运算01 插入[C++]

本文介绍了如何使用位运算解决LeetCode上的一个题目,通过循环清零、左移和按位或操作,将给定整数M插入到整数N的特定二进制位区间,实现高效算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图源:文心一言

上机题目练习整理,位运算,供小伙伴们参考~🥝🥝

网页版目录在页面的右上角↗~🥝🥝

  • 第1版:在力扣新手村刷题的记录~🧩🧩

编辑:梅头脑🌸

审核:文心一言

题目:面试题 05.01. 插入 - 力扣(LeetCode)


🧵面试题 05.01. 插入 - 力扣(LeetCode)

🧩题目

给定两个整型数字 N 与 M,以及表示比特位置的 i 与 ji <= j,且从 0 位开始计算)。

编写一种方法,使 M 对应的二进制数字插入 N 对应的二进制数字的第 i ~ j 位区域,不足之处用 0 补齐。具体插入过程如图所示。

题目保证从 i 位到 j 位足以容纳 M, 例如: M = 10011,则 i~j 区域至少可容纳 5 位。

示例1:

 输入:N = 1024(10000000000), M = 19(10011), i = 2, j = 6
 输出:N = 1100(10001001100)

示例2:

 输入: N = 0, M = 31(11111), i = 0, j = 4
 输出:N = 31(11111)

🌰方法一:循环+按位运算

📇算法思路

  • 算法思想:
    • 将N的第i位到第j位清零(置为0),可以使用按位与和按位取反操作。
    • 将M左移,使得它的最低位对齐N的第i位。
    • 使用按位或操作,将步骤1和步骤2的结果合并。
  • 时间复杂度:O(1);
  • 空间复杂度:O(1);

⌨️算法代码

class Solution {
public:
    int insertBits(int N, int M, int i, int j) {
        // 1.把N的i到j位置为0
        for (int k = i; k <= j; k++) {
            if (N & (1 << k)) {
                N ^= (1 << k);
            }
        }
        // 2.把M的数值左移i位
        M <<= i;
        // 3.将N的i到j位加上M
        return N | M;
    }
};

作者:coeker
链接:https://leetcode.cn/problems/insert-into-bits-lcci/solutions/712628/cwei-yun-suan-by-coeker-stgw/

📇代码解释

 1:把N的i到j位置为0

  • N & (1 << k)):N的2~6位 (&)与 1,可以区分2~6位是0还是1;如果2到6为1,满足判断条件,则执行异或语句;
  • N ^= (1 << k):将N的2~6位中二进制为“1”的位数 异或(1)1,根据同1异0,这些位置都会变为0,达到置零效果;
  • 执行结果:虽然本题N = 1024,其2~6位已经是0,条件语句执行前后的结果是完全相同的,还是1024;
N
31302928272625242322212019181716
0000000000000000
15141312111009080706050403020100
0000010000000000

2:把M的数值左移i位

  • M <<= i; // 本题中i = 2;正数左移低位可以直接补0;
M移动前
31302928272625242322212019181716
0000000000000000
15141312111009080706050403020100
0000000000010011
M移动后
31302928272625242322212019181716
0000000000000000
15141312111009080706050403020100
0000000001001100

3:将N的i到j位加上M

  • N | M;  // N 或 M,对应位中,如果其中有1个是1,则或的运算结果为1;
  • 输出结果:N = 1100(10001001100)
N | M
31302928272625242322212019181716
0000000000000000
15141312111009080706050403020100
0000100001001100

📇知识扩展

位运算是直接对整数在内存中的二进制位进行操作的一种运算。由于它直接处理二进制数据,因此执行效率非常高。

简单介绍:

  1. 取反(NOT)
    • 操作符:~
    • 功能:对一个数的所有二进制位进行取反操作,即0变为1,1变为0。
    • 示例:~0101 结果为 1010(这里的数字是二进制表示)。
  2. 与(AND)
    • 操作符:&
    • 功能:比较两个数的相应二进制位,如果两个相应位都为1,则该位的结果值为1,否则为0。
    • 示例:0101 & 0110 结果为 0100
  3. 或(OR)
    • 操作符:|
    • 功能:比较两个数的相应二进制位,如果两个相应位中至少有一个为1,则该位的结果值为1,否则为0。
    • 示例:0101 | 0110 结果为 0111
  4. 异或(XOR)
    • 操作符:^
    • 功能:比较两个数的相应二进制位,如果两个相应位值不同,则该位的结果值为1,否则为0。
    • 示例:0101 ^ 0110 结果为 0011
  5. 移位操作
    • 左移(Left Shift):<<
      • 功能:将二进制数所有位向左移动指定的位数,高位丢弃,低位用0补充。
      • 示例:0010 << 1 结果为 0100
    • 右移(Right Shift):>>(有符号和无符号之分,具体行为取决于编程语言)
      • 功能:将二进制数所有位向右移动指定的位数,低位丢弃,高位可能用0或1补充(取决于原数的符号)。
      • 示例:0010 >> 1 结果为 0001

特殊用途

  • 指定位置0
    • 使用与运算(AND)可以将指定位置0。
    • 示例:假设要将数num的第i位(从0开始计数)置0,可以使用num & ~(1 << i)
  • 指定位置1
    • 使用或运算(OR)可以将指定位置1。
    • 示例:假设要将数num的第i位(从0开始计数)置1,可以使用num | (1 << i)
  • 指定位翻转
    • 使用异或运算(XOR)可以实现指定位的翻转(0变为1,1变为0)。
    • 示例:假设要翻转数num的第i位(从0开始计数),可以使用num ^ (1 << i)

位运算在编程中有很多用途,包括优化性能、处理二进制数据、加密解密、网络通信中的数据打包与解包等。

🌰解题二:创建掩码<错误代码自留>

📇算法思路

  • 基本思想同算法一,不再赘述~
  • 这个代码提交会出错,自留,以后可能会回来修改~

⌨️算法代码

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

class Solution {
public:
    int insertBits(int N, int M, int i, int j) {
        // 确保 i 和 j 是有效的索引  
        assert(0 <= i && i <= j && j < 32);

        // 创建掩码以清零 N 的第 i 位到第 j 位  
        unsigned int allOnes = ~0U; // 使用无符号整数  
        unsigned int leftOnes = allOnes >> (j + 1); // 现在这是安全的,因为 allOnes 是无符号的  
        unsigned int rightOnes = ((1U << i) - 1); // 注意这里应该是 (1 << i),并且使用无符号整数  
        unsigned int mask = leftOnes | rightOnes;

        // 将 N 的第 i 位到第 j 位清零  
        N = N & mask;

        // 将 M 左移,使得它的最低位对齐 N 的第 i 位  
        M = M << i;

        // 使用按位或操作,将步骤1和步骤2的结果合并  
        N = N | M;

        // N 现在是一个无符号整数,但我们知道它不会溢出,所以可以安全地将其转换回有符号整数  
        return static_cast<int>(N);
    }
};

int main() {
    Solution solution;

    int N = 1024; // 10000000000  
    int M = 19; // 10011  
    int i = 2;
    int j = 6;

    int result = solution.insertBits(N, M, i, j);
    cout << "Output: N = " << result << " (in decimal, expected binary representation: 10001001100)" << endl;

    // 第二个示例  
    N = 0;
    M = 31; // 11111  
    i = 0;
    j = 4;

    result = solution.insertBits(N, M, i, j);
    cout << "Output: N = " << result << " (in decimal, expected binary representation: 11111)" << endl;

    return 0;
}

 作者:文心一言

📇代码解释

1:清零 N 的第 i 位到第 j 位

  • unsigned int allOnes = ~0U; ~是按位非操作,无符号0的二进制为00...000,按位取反后为11...111,本步骤创建一个全是1的掩码;
  • unsigned int leftOnes = allOnes << (j + 1);  allOnes左移 j + 1 位,创建左边全是1,到 j + 1 位为0的掩码,本例 j = 6 ;(备注一下,这里如果是int,那么就需要逻辑左移而非算术左移,否则负数左移补1,无论它怎么左移它都是111...111);
  • int rightOnes = (1 << i) - 1; // 1左移2位然后-1,创建右边全是1,到i位为0的掩码 ,本例 i = 2;
allOnes
31302928272625242322212019181716
1111111111111111
15141312111009080706050403020100
1111111111111111
leftOnes
31302928272625242322212019181716
1111111111111111
15141312111009080706050403020100
1111111110000000
rightOnes
31302928272625242322212019181716
0000000000000000
15141312111009080706050403020100
0000000000000011
  • unsigned int mask = leftOnes | rightOnes;  // leftones与rightones相与,掩码就这样制作好了,是从2到6为0,其余都为1的数字;
  • N = N & mask; // 将 N 的第 i 位到第 j 位清零(当然1024的2-6位本来就没1,清0前后是一样的);
mask
31302928272625242322212019181716
1111111111111111
15141312111009080706050403020100
1111111110000011
N(修改后)
31302928272625242322212019181716
0000000000000000
15141312111009080706050403020100
0000010000000000

2:把M的数值左移i位

3:将N的i到j位加上M

备注:2:与3:的步骤与方法一雷同,此处不再赘述。

执行报错:我暂时也不知道怎么修复这个,哎,先留着代码再想想...

📇知识扩展

思维导图:emmm...补码规则听起来有点绕对不对,而且正数负数的存储规则是不一样的,这就涉及到计组的知识了,正好是我来不及整理和发布的那一篇,稍等我Po一下草稿——

备注:


🔚结语

博文到此结束,写得模糊或者有误之处,欢迎小伙伴留言讨论与批评,督促博主优化内容{例如有错误、难理解、不简洁、缺功能}等,博主会顶锅前来修改~~😶‍🌫️😶‍🌫️

我是梅头脑,本片博文若有帮助,欢迎小伙伴动动可爱的小手默默给个赞支持一下,感谢点赞小伙伴对于博主的支持~~🌟🌟

同系列的博文:🌸数据结构_梅头脑_的博客-CSDN博客

同博主的博文:🌸随笔03 笔记整理-CSDN博客

<think>好的,我现在需要帮助用户了解C++位运算的使用方法和示例。首先,我应该回顾用户提供的引用内容,看看有哪些相关的信息可以整合进去。用户给出了五个引用,涉及到位运算的不同方面,比如或运算、异或运算、位图应用以及位移操作等。 首先,我需要明确位运算的基本操作符,包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)、左移(<<)和右移(>>)。每个操作符的用途和示例都需要详细说明,同时结合用户提供的引用中的代码示例,这样能让回答更具体。 例如,引用[1]提到了或运算的C++示例,可以用于设置特定比特位。引用[2]和[3]展示了异或在加法算法和找出只出现一次的数字中的应用。引用[4]中的位图思想用于检测字符串是否唯一,这可以用位掩码和或运算来实现。引用[5]提到了位移运算符的用途,比如左移实现乘2的幂次。 接下来,我需要将这些知识点组织起来,分门别类地解释每个位运算符的作用,并给出对应的示例代码。要注意用户提到的系统级指令,比如行内数学表达式用$...$,独立公式用$$...$$,但用户提供的引用中并没有数学公式,所以可能需要根据情况添加,比如解释异或的性质时可以用数学表达式。 另外,用户要求生成相关问题,我需要根据内容设计几个相关的问题,比如位运算的常见应用场景、如何用位运算优化代码等。 在编写过程中,要确保语言简洁,结构清晰,符合中文表达习惯。同时,引用需要自然地插入到相关内容末尾,如引用[^1]、[^2]等。需要检查每个引用对应的内容是否正确,比如引用[2]是关于使用异或和与运算实现加法,引用[3]是关于异或的恒等律应用。 最后,确保所有代码示例使用正确的C++语法,并符合用户提供的引用中的代码风格。例如,引用[4]中的位图实现需要详细说明每个步骤的作用,如何通过位运算判断字符是否重复出现。 总结来说,我需要分步骤介绍每个位运算符,结合用户提供的引用中的示例,说明其实际应用,并确保回答结构清晰,引用正确,同时生成相关问题以帮助用户进一步学习。</think>### C++ 位运算详解 位运算直接操作二进制位,常用于高效处理整数、状态压缩、位掩码等场景。以下是常见位运算符及示例: --- #### 一、基本运算符与用途 1. **按位与(`&`)** 对应位同为1时结果为1,否则为0。 **用途**:提取特定位、判断奇偶性。 **示例**: ```cpp int a = 5; // 二进制 0101 int b = 3; // 二进制 0011 int c = a & b; // 结果 0001(十进制1) ``` 2. **按位或(`|`)** 对应位至少一个为1时结果为1。 **用途**:设置特定位。 **示例**(引用[1]): ```cpp int bit = 0; bit |= (1 << 3); // 将第3位设为1(二进制1000) ``` 3. **按位异或(`^`)** 对应位不同时结果为1。 **性质**: - 恒等律:$a \oplus 0 = a$ - 自反性:$a \oplus a = 0$ **用途**:交换变量、找唯一数、加法模拟。 **示例**(引用[3]): ```cpp int arr[] = {1, 2, 3, 2, 1}; int num = 0; for (int x : arr) num ^= x; // 结果3(唯一出现一次的数) ``` 4. **按位取反(`~`)** 所有位取反。 **示例**: ```cpp int a = 5; // 0101 int b = ~a; // 1010(补码形式,实际值为-6) ``` 5. **左移(`<<`)** 所有位左移,低位补0。 **用途**:快速计算$2^n$(引用[5])。 **示例**: ```cpp int a = 3; // 0011 int b = a << 2; // 1100(十进制12,即3×2²) ``` 6. **右移(`>>`)** 所有位右移,高位补符号位(算术右移)或0(逻辑右移)。 **示例**: ```cpp int a = -8; // 1111...1000 int b = a >> 2; // 1111...1110(十进制-2) ``` --- #### 二、实际应用场景 1. **快速加法**(引用[2]) 通过异或和与运算模拟进位: ```cpp int getSum(int a, int b) { while (b) { int carry = (a & b) << 1; // 计算进位 a = a ^ b; // 无进位相加 b = carry; } return a; } ``` 2. **位图压缩状态**(引用[4]) 用整数比特位表示字符是否出现: ```cpp bool isUnique(string s) { int bit = 0; for (char ch : s) { int idx = ch - 'a'; if ((bit >> idx) & 1) return false; // 判断是否重复 bit |= (1 << idx); // 标记已出现 } return true; } ``` 3. **权限控制** 使用位掩码管理权限: ```cpp const int READ = 1 << 0; // 0001 const int WRITE = 1 << 1; // 0010 int user_permission = READ | WRITE; // 赋予读写权限 bool canWrite = user_permission & WRITE; // 检查写权限 ``` --- #### 三、注意事项 - **优先级**:位运算符优先级通常低于算术运算符,建议用括号明确顺序。 - **符号位**:右移操作对有符号数的结果依赖编译器实现。 - **可读性**:复杂位运算需添加注释说明逻辑。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值