【算法篇】位运算 —— 基础篇

作者主页

|--------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| 作者简要描述 | |
| 算法篇 | Linux篇 |
| QT百日筑基篇 | 数据结构篇 |

基础位运算


目录/索引:

目录

基础位运算

目录/索引:

位运算宝典:

[位运算宝典1: 基础位运算](#位运算宝典1: 基础位运算)

[位运算宝典2: 给定一个数n , 确定他的二进制位的第x位是1还是0.](#位运算宝典2: 给定一个数n , 确定他的二进制位的第x位是1还是0.)

位运算宝典3:给定一个数将他的第x位修改为1.

位运算宝典4:给定一个数将他的第x位修改为0.

位运算宝典5:位图的思想。

位运算宝典6:提取一个数(n)二进制表示最右侧的1

位运算宝典7:干掉一个数(n)二进制表示最右侧的1

位运算宝典8:位运算的优先级

位运算宝典9:异或运算的运算律。

[题目一: 191. 位1的个数](#题目一: 191. 位1的个数)

题目解析:

算法原理:

编写代码:

运行/提交:

[题目二:338. 比特位计数](#题目二:338. 比特位计数)

题目解析:

算法原理:

编写代码:

运行/提交:

[题目三:461. 汉明距离](#题目三:461. 汉明距离)

题目解析:

算法原理:

编写代码:

运行/提交:

扩展:__builtin_popcount()

[题目四:136. 只出现一次的数字](#题目四:136. 只出现一次的数字)

题目解析:

算法原理:

编写代码:

运行/提交



在学习位运算时我们要首先的了解一些位运算的基础知识.

位运算宝典:

位运算宝典1: 基础位运算

>> : 左移

特点: > 尖部分指向左侧所以是左移,一般数左移会将自身缩小2倍。

例如: 2 >>1 0010 -> 0001 从2到一。 >>2位就是2的次方倍 4>>2 ==1。

<< : 右移

特点: > 尖部分指向左侧所以是右移,一般数左移会将自身扩大2倍。

例如: 2 <<1 0010 -> 0100 从2到4。

~ : 取反

特点: 将自身的二进制进行相反的处理, 原来是1变0,是0变1.

例如: 0101 ------>取反 1010.

& : 按位与

特点: 有0为0,两个都是1才为1.

例如:0011 & 0101 = 0001.

| :按位或

特点: 有1为1,两个都是0才为0.

例如:0011 | 0101 = 0111.

^ : 按位异或

特点:相同为0, 相异为1。 / 无进位相加。

例如:0011 ^ 0101 = 0110.

位运算宝典2: 给定一个数n , 确定他的二进制位的第x位是1还是0.

答案: (n>>x) &1 是0则为0 是1则为1.

位运算宝典3:给定一个数将他的第x位修改为1.

答案: n |= (1<<x)

位运算宝典4:给定一个数将他的第x位修改为0.

答案:n &= ~(1<< x)

位运算宝典5:位图的思想。

答案:哈希思想。 给定一个int能表示32位的比特位。

如果一个数组的第二位是否有数,我们只要查看这个int的第二位是否为1即可。

位运算宝典6:提取一个数(n)二进制表示最右侧的1

答案: n & -n

位运算宝典7:干掉一个数(n)二进制表示最右侧的1

答案: n & ((~n)+1) / n&(n-1)

位运算宝典8:位运算的优先级

答案: 管什么优先级能加括号就加括号。

~ > << >> > & > ^ > | 尽量加括号

位运算宝典9:异或运算的运算律。

答案: 异或遵循异或交换律。

题目一: 191. 位1的个数

题目解析:

题目所人话就是给一个数让我们看看他的二进制有几个1.

算法原理:

方法一:

我们可以循环的进行移动。添加一个计数的变量。我们将n和1进行&如果这个结果为1,就将这个计数的变量进行++。然后继续的进行左移。 循环结束的标志就是我们的n为0.为0了就代表后面没有1了。

方法二:(速度更快)

采用宝典7。 我们用宝典7,每一次都干掉最右侧的1. 知道将所有的1都干掉。结果也就为0了。

编写代码:

方法一:

cpp 复制代码
class Solution {
public:
    int hammingWeight(int n) 
    {
        int ret;
        while(n)
        {
           if(n & 1) ret++;
           
           n = n >> 1;
        } 
        return ret;   
    }
};

方法二:

cpp 复制代码
class Solution {
public:
    int hammingWeight(int n) {
        int cnt = 0;
        while (n) {
            cnt++;
            n &= n - 1;
        }
        return cnt;
    }
};

运行/提交:

题目二:338. 比特位计数

题目解析:

我们从0到n这中间的数所包含1的个数。 将他们放到ans数组中。

例如 : 1 : 0001 包含1个1所以我们的返回数组ans1=1;

3: 0101 包含两个1 所以我们的返回数组ans3=2;

算法原理:

这里我们要进行将所有的数组放到ans中所以要进行遍历一边这个数组。一定要进行一个for。

我们要在内部是判断这个i位置有几个1这就回到我们题目一的方法了。当然我们这里要创建一个数组来进行返回。我这里习惯将vector命名为dp

编写代码:

cpp 复制代码
class Solution {
public:
    vector<int> countBits(int n) 
    {
        vector<int> dp(n + 1);
        dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            int ret=0,n=i;
            while(n)
            {
                n &= (n - 1);
                ret+=1;
            }
            dp[i]=ret;
        }    
        return dp;
    }
};

运行/提交:

题目三:461. 汉明距离

题目解析:

我们计算一个数二进制的两个数的不同位置。

算法原理:

我们可以先让x和y疑惑一下。这样可以让我们的不同位置直接的变成1.然后我们直接找异惑玩的位置是1就好了, 即此题就是在找异惑1的个数。

我们让x异或完的数的值给s, 我们找s中的1的个数就好了,s&s-1是最简单的找1的因为&后最前面的1会直接的进行消除。我们这时候创建一个变量就行了。计数然后最后的时候进行返回就好了。

编写代码:

cpp 复制代码
class Solution {
public:
    int hammingDistance(int x, int y) 
    {    
        //取出最右侧的1的位置
        int s =x ^y;
        int ret=0;
        while(s)
        {
           s&=s-1;
           ret++;
        }
        return ret;
    }
};

运行/提交:

扩展:__builtin_popcount()

这里提供一个函数__builtin_popcount()是GCC/Clang 编译器内置函数 ,不属于标准库,不用包含任何头文件就能直接使用 。 只要是 .c / .cpp 文件,正常编译即可调用。

builtin:执行内建的函数, popcount:爆米花

作用: 计算一个数字中二进制形式1的个数。

ai关键词:__builtin_popcount介绍一下这个函数,并给出如何去使用。

函数的说明:豆包ai生成

__builtin_popcount 完整介绍与使用教程

一、基础介绍

1. 作用

GCC/Clang 编译器内置函数,计算一个无符号整数二进制中 1 的个数(汉明重量),底层调用 CPU popcnt 硬件指令,速度远快于手写循环统计。

2. 三个版本(严格区分类型,混用会出错)

函数名 参数类型 适用整数范围
__builtin_popcount unsigned int(32 位) 普通 int 数字
__builtin_popcountl unsigned long(64 位 long) long 类型
__builtin_popcountll unsigned long long(64 位长长整型) 大数、状压 DP 常用

3. 关键特性

  1. 无需头文件:编译器内置,不用 #include 任何库;

  2. 仅 GCC/Clang 可用:Windows MSVC (VS) 不支持;

  3. 禁止直接传负数:有符号负数会按补码处理,统计出大量 1,必须强转无符号。

cpp 复制代码
class Solution {
public:
    int hammingDistance(int x, int y) 
    {    
        return __builtin_popcount(x^y);
    }
};

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

题目解析:

给定我们一个数组, 我们要在这个数组中找到哪个只出现一次数字的那个数字。

算法原理:

我们注意到^,x^x=0, 0^x=x;这两个性质,并且异或支持交换率

所以我们可以创建一个ret让他等于0,然后每一次进入数组都进行一次异或。

图解:

编写代码:

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) 
    {
        int ret=0;
        for(auto &e:nums)
        {
            ret ^=e;
        }
        return ret;
    }
};

运行/提交

相关推荐
youtootech7 小时前
HarmonyOS《柚兔学伴》项目实战08-关系型数据库入门——待办事项持久化
jvm·数据库·华为·harmonyos
laboratory agent开发7 小时前
企业AI Agent落地前,先回答四个工程问题
java·前端·人工智能
Black蜡笔小新7 小时前
算法训练完了怎么上线?DLTM→AIS→EasyGBS三步交付流水线
人工智能·算法
微三云 - 廖会灵 (私域系统开发)7 小时前
电商系统国际化架构设计:多语言、多币种、多时区、多税制的全链路实现
java·前端·数据库
XS0301067 小时前
Spring AI 第二课-流式输出 & 运行时动态参数配置
java·人工智能·spring
额恩667 小时前
阶段五:HttpOnly Cookie 登录持久化
java·spring
sycmancia7 小时前
Qt——多线程与界面组件的通信
开发语言·qt·算法
周杰伦的稻香7 小时前
PostgreSQL中的METHOD(认证方法)
数据库·postgresql
唐青枫7 小时前
Java Netty 实战指南:从 NIO 线程模型到 TCP 编解码和心跳机制
java