
|--------------------------------------------------------------------------------|------------------------------------------------------------------------------|
| 作者简要描述 | |
| 算法篇 | Linux篇 |
| QT百日筑基篇 | 数据结构篇 |
基础位运算
目录/索引:
目录
[位运算宝典1: 基础位运算](#位运算宝典1: 基础位运算)
[位运算宝典2: 给定一个数n , 确定他的二进制位的第x位是1还是0.](#位运算宝典2: 给定一个数n , 确定他的二进制位的第x位是1还是0.)
[题目一: 191. 位1的个数](#题目一: 191. 位1的个数)
[题目二:338. 比特位计数](#题目二:338. 比特位计数)
[题目三:461. 汉明距离](#题目三:461. 汉明距离)
[题目四: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_popcountunsigned int(32 位)普通 int 数字 __builtin_popcountlunsigned long(64 位 long)long 类型 __builtin_popcountllunsigned long long(64 位长长整型)大数、状压 DP 常用 3. 关键特性
无需头文件:编译器内置,不用
#include任何库;仅 GCC/Clang 可用:Windows MSVC (VS) 不支持;
禁止直接传负数:有符号负数会按补码处理,统计出大量 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;
}
};
运行/提交

