算法沉淀第三天(统计二进制中1的个数 两个整数二进制位不同个数)

目录

引言:

统计二进制中1的个数

题意分析

逻辑梳理

第一种

第二种

第三种

代码实现

第一种

第二种

第三种

俩个整数二进制位不同个数

题意分析

逻辑梳理

代码实现

结语:


引言:

今天是算法沉淀的第三天,跟之前几天不同的是,今天不强化思维训练了,今天训练位操作符的使用,今天主要讲解俩个题目,分别牛客的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;
}

那么,这题也讲完啦


结语:

今日算法讲解到此结束啦,希望对你们有所帮助,谢谢观看,如果觉得不错可以分享给朋友哟。有什么看不懂的可以评论问哦,

相关推荐
MicroTech20253 小时前
微算法科技MLGO推出隐私感知联合DNN模型部署和分区优化技术,开启协作边缘推理新时代
科技·算法·dnn
小冯记录编程3 小时前
深入解析C++ for循环原理
开发语言·c++·算法
磨十三4 小时前
C++ 容器详解:std::list 与 std::forward_list 深入解析
开发语言·c++·list
今麦郎xdu_4 小时前
【Linux系统】命令行参数和环境变量
linux·服务器·c语言·c++
chenchihwen5 小时前
深度解析RAG系统中的PDF解析模块:Docling集成与并行处理实践
python·算法·pdf
情深不寿3176 小时前
C++特殊类的设计
开发语言·c++·单例模式
做科研的周师兄6 小时前
【机器学习入门】7.4 随机森林:一文吃透随机森林——从原理到核心特点
人工智能·学习·算法·随机森林·机器学习·支持向量机·数据挖掘
Vanranrr6 小时前
nullptr vs NULL:C/C++ 空指针的演变史
c语言·c++
切糕师学AI6 小时前
【多线程】阻塞等待(Blocking Wait)(以C++为例)
c++·多线程·并发编程·阻塞等待