目录
引言:
今天是算法沉淀的第三天,跟之前几天不同的是,今天不强化思维训练了,今天训练位操作符的使用,今天主要讲解俩个题目,分别牛客的JZ15和牛客的REAL520,这俩题都是跟位运算有关的相关题目,因为JZ15(即统计二进制中1的个数)这题是交互型提交方式,因为我直接是全码写的,所以就不再搓一遍交题了。
我们先讲简单的一道题,然后通过简单题的分析,我们再进入比较难的题的讲解
那么接下来,话不多说,我们就进入今天的算法讲解--------------------->

统计二进制中1的个数
按照惯例,先看题目
题意分析
因为题目目的太清晰了,题目就直接放图啦

题目想要我们做的很简单,就是输入一个数,然后要知道这个数存入计算机后,二进制表示的这个数有多少个1,然后输出1的个数就好了
那么题目就说完了,接下来我们进入逻辑梳理环节
逻辑梳理
在做这题之前,我们需要知道一个点,那就是计算机存入的整数的二进制存的是这个数的补码,而不是原码,例如-1的源码是10000000000000000000000000000001,但计算机存的是他的补码,即11111111111111111111111111111111.
弄清这一点之后,我们就可以开始思考怎么来找1的个数了
首先,最原始的暴力的方法就不讲了
接下来,这题其实有3种思路可以打,这3种思路会由简单到难,接下来的代码也会有3种思路的不同打法
第一种
首先整数的补码是和原码一样的,所以我们可以先不看负数,先看正数,那么正数的1到个数就相当于是2进制,2进制我们不熟悉,但是我们十进制熟悉呀
所以我们先去想十进制的1的规律,如图

那么十进制的规律已经找出来了,那么二进制是不是也一样,那就只需要把10改成2,用循环一直走走到0结束,然后输出1的个数就可以了
**但是,这样的话还是有严重的数据范围问题,那就是负数怎么办,这个处理起来也简单,虽然这种方式不太好,但依旧是处理问题的好方式,那就是将数据类型从int 改为unsigned int,**因为整数转成二进制后的最高位代表的是符号位,但是如果将int改为unsigned int的话,在数据运算时,二进制的最高位编代表的不是符号位了,而是数据位,所以就相当于是把一个负数变成了一个很大的整数来进行运算
那么,最简单的一种思路就讲解完毕啦,接下来,我们来讲第二种思路
第二种
第二种思路相比于第一种思路来说,需要用到移位操作符和位操作符了
首先因为数据范围是int类型的,所以我们只需要对这个询问的数进行移位就可以了,然后每次移位就跟1进行&操作,通过每次移位后最低位和1的&,循环结束时所有的位都和1进行&了,如果每次判断得到的结果是1,就对答案++,循环结束之后输出结果就可以了
那么第二种思路也讲解完毕啦,接下来,我们来讲第三种思路
第三种
第三种思路就比较有意思了,他需要用到一个式子,即n=n&(n-1),这个式子是很玄妙的,每次进行一次这个操作,便可以将最低位的1去除掉,所以只需要在外面开一个循环,然后循环的判断条件为n,然后循环内部就放这一个式子以及答案的+1,等循环结束(即n为0)后,输出的答案就是1的个数了
这个思路的巧妙点就在于n=n&(n-1)这个式子的使用,这个式子通过简单的推理是马上就能得到一个结论的,即每次进行这个式子操作便可以将最低位的1去除掉,至于如何推导,这里就不推导了,有兴趣的可以用正整数来推导一下,正整数推导简单点,负数推导比较麻烦
那么第三种思路也讲解完毕啦,接下来,我们就进入代码实现的环节
代码实现
那么,代码实现也与上面三种逻辑一一对应,因为需要注意的逻辑梳理里都讲了,所以这里就直接展示代码啦,首先我们先来看第一种
第一种
cpp
#include <iostream>
using namespace std;
int main()
{
unsigned int n;
cin>>n;
int ans = 0;
while(n)
{
if(n%2==1)
ans++;
n/=2;
}
cout<<ans<<endll;
return 0;
}
第二种
cpp
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int ans = 0;
for(int i = 0;i<32;i++)
{
if(((n>>i)&1)==1)
ans++;
}
cout<<ans<<endll;
return 0;
}
第三种
cpp
#include <iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int ans = 0;
while(n)
{
n = n & (n - 1);
ans++;
}
cout<<ans<<endll;
return 0;
}
那么,这题就讲完啦,我们接着来看下一题
俩个整数二进制位不同个数
按照惯例,我们先看题目
题意分析
题目如图
题目就是给我们2个数,然后问我们这俩个数存入计算机内存后,有几个位是不同的
然后输出不同位的个数就好了
那么题目意思讲完了,接下来我们进入逻辑梳理环节
逻辑梳理
这题需要我们找出不同位的个数,有俩种方法,一种是直接找,另一种是找到相同位点个数,然后再用32减去相同位的个数,其实这俩个没什么区别,毕竟都是一趟循环的事,我用的是第二种的方法
那么,这题需要用到的方法就是上面那题所讲的第二种逻辑,只需要在那个逻辑的基础上修改一下就好了,因为我是要先找出相同位的个数,那就是对俩个数都用移位操作符,然后将移位完后的数和1进行&操作,然后如果得到的俩个值一样,就说明俩个值在对应的这个位上的数是一样的
然后在循环完后,用32减去一样的位的个数就可以得到最终答案啦,因为题目已经给了数据类型是int了
那么逻辑梳理完了,接下来就进入代码实现环节啦
代码实现
这里就直接放源码啦
cpp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
int ans = 0;
for (int i = 0; i < 32; i++)
{
if (((a >> i) & 1) == ((b >> i) & 1))
ans++;
}
cout << 32 - ans << endl;
return 0;
}
那么,这题也讲完啦
结语:
今日算法讲解到此结束啦,希望对你们有所帮助,谢谢观看,如果觉得不错可以分享给朋友哟。有什么看不懂的可以评论问哦,
