当前位置: 首页 > article >正文

二进制位运算题

在这里插入图片描述

本期介绍🍖
主要介绍:1. 在不创建临时变量的情况下交换两个变量,2. 计算变量在内存中存放2进制位“1”的个数,3. 求两个数的二进制中不同位的个数,4. 分别打印整数的二进制中奇数位和偶数位,5. 判断一个整数是不是2的n次方,6. 在成对元素的数组中找单身狗。


文章目录

  • 1. 交换两个变量(不创建临时变量)
    • 1.1 加减法
    • 1.2 异或法
  • 2. 计算变量在内存中存放2进制位“1”的个数
    • 2.1 除二取余法
    • 2.2 移位与1法
    • 2.3 消1计数法
  • 3. 求两个数二进制中不同位的个数
  • 4. 打印整数二进制的奇数位和偶数位
  • 5. 判断一个整数是不是2的n次方
  • 6. 在成对元素的数组中找单身狗
    • 6.1 题目1
    • 6.2 题目2


1. 交换两个变量(不创建临时变量)

  题目: 交换两个整数的内容,在交换的过程中不允许创建第三个临时变量。
  一般想要交换两个变量,会通过创建第三个临时变量来完成,就如同想要将两瓶饮料进行交换,需要第三个空瓶一样。代码实现如下所示:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int tmp = 0;
	printf("交换前:a=%d, b=%d\n", a, b);
	//交换变量a和变量b
	tmp = a;
	a = b;
	b = tmp;
	printf("交换后:a=%d, b=%d\n", a, b);
	return 0;
}

在这里插入图片描述


1.1 加减法

  解题思路: 求出两个变量的和,通过和与变量的差,在不创建第三个临时变量的情况下交换两个变量。代码如下所示:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d, b=%d\n", a, b);
	//交换变量a和变量b
	a = a + b;
	b = a - b;
	a = a - b;
	printf("交换后:a=%d, b=%d\n", a, b);
	return 0;
}

在这里插入图片描述

  其实不难看出加减法是存在缺陷的,当a和b的值都很大的时候(即都非常接近int类型所能表示范围中的最大值),那么a+b的值必然会超过int型所能存放的极限,导致一些二进制位的丢失,最后交换的结果必然会是错误的。


1.2 异或法

  所谓的异或法就是通过异或操作符符 “ ^ ” 来实现交换两变量中的值。在这里不得不说一下异或运算符的一些性质:

  性质1: 计算3 ^ 3表达式,3的二进制位为011,那两个同为011的二进制位进行异或操作,结果必然会是000;同理5 ^ 5的结果也是000,所以不难看出异或的第一条性质为:x^x = 0

  性质2: 计算3 ^ 0表达式,3的二进制位为011,0的二进制位为000,进行异或操作结果为011,也为3。由此发现不管什么数与0进行异或结果必然会是该数本身,所以异或的第二条性质为:x^0=a

  性质3: 表达式(3 ^ 3 ^ 5)的计算结果为5,表达式(3 ^ 5 ^ 3)的结果也为5。所以不难看出异或的第三条性质(交换律):(x ^ x ^ y) = (x ^ y ^ x)

  代码实现如下所示:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d, b=%d\n", a, b);
	//交换变量a和变量b
	a = a ^ b;//a里面存放a^b
	b = a ^ b;//此时的a^b其实是(a^b)^b,结果为a
	a = a ^ b;//此时的a为a^b,b为a,故a^b其实是(a^b)^a,结果为b
	printf("交换后:a=%d, b=%d\n", a, b);
	return 0;
}

在这里插入图片描述

  其实可以将a^b得到的结果看作是一个秘盒,a与b通过异或与秘盒结合都可以得到另个数。异或法就没有加减法的缺陷,不会因数据过大使得存储不下。


2. 计算变量在内存中存放2进制位“1”的个数

  题目:输入一个整数 n ,计算n在内存中存放的二进制位中1的个数(其中负数用补码表示)。下面会用3种不同的方法来解决这道笔试题。


2.1 除二取余法

  在数学中要将十进制数转换成二进制数老师肯定教过一种方法“ 除2取余法 ”。举例如下图所示,计算十进制89化为二进制数。除完所有数,把所有余数从下往上排序后,得到的结果就是89的二进制表示形式为:1011001。
在这里插入图片描述
  引用此法,每次除2后统计余数为1的情况,这样就可以做到计算变量在内存中存放2进制位为“1”的个数啦。代码如下:

#include<stdio.h>
int sta_bit_num(int n)
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n /= 2;
	}
	return count;
}
int main()
{
	int n = 0;
	int count = 0;
	printf("请输入要计算的数:");
	scanf("%d", &n);
	count = sta_bit_num(n);
	printf("二进制中1的个数:%d\n", count);
}

在这里插入图片描述
  乍一看似乎没什么问题,但当输入负数时就会发生错误。就譬如输入-1。如下图所示,得出的结果为0,如下图所示:

在这里插入图片描述

  由于在编写代码时,压根就没有思考过负数的情况。而负数再内中存放的补码的形式与正数是不同的,所以如果还按照原先计算正数的除2取余法来计算负数必然错误。解决方法:将输入的有符号数强制类型转换为无符号数,用除2取余法再对该无符号数进行统计就间接达到效果了。代码如下:

#include<stdio.h>
int sta_bit_num(unsigned int n)
{
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n /= 2;
	}
	return count;
}
int main()
{
	int n = 0;
	int count = 0;
	printf("请输入要计算的数:");
	scanf("%d", &n); 
	count = sta_bit_num(n);
	printf("二进制中1的个数:%d\n", count);
}

在这里插入图片描述


2.2 移位与1法

  解题思路:一个数只要" &1 "就可以求得该数在内存中存放的二进制位的最低位,再通右移操作符“ >> ”就可以统计32位上所有1的个数了。如下图所示:

在这里插入图片描述
  代码如下:

#include<stdio.h>
int sta_bit_num(int n)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if ((n >> i) & 1 == 1)
		{
			count++;
		}
	}
	return count;
}
int main()
{
	int n = 0;
	int count = 0;
	printf("请输入要计算的数:");
	scanf("%d", &n);
	count = sta_bit_num(n);
	printf("二进制中1的个数:%d\n", count);
}

在这里插入图片描述

  值得注意,移位与1法会做多余的判断,不管被求的那个数在内存中的二进制位有几个1,始终都是要循环判断32次的。


2.3 消1计数法

  根据题意可知,所需统计的仅仅只是二进制位中1的个数,但上面两种方法在判断统计二进制位1的同时还会多余的判断二进制位0。
  现在讲解一种非常牛逼的算法“ 消1计数法 ”。该方法是将被求的数" & "比被求数小1的数,即:n & (n-1) 。计算结果会让二进制序列中最低位的那个1变为0。如果再多来几次这样的操作,被求的那个数最终会变成0,这样做就效率不就上来了嘛。如下图所示:
在这里插入图片描述
  代码为:

#include<stdio.h>
int sta_bit_num(int n)
{
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	return count;
}
int main()
{
	int n = 0;
	int count = 0;
	printf("请输入要计算的数:");
	scanf("%d", &n);
	count = sta_bit_num(n);
	printf("二进制中1的个数:%d\n", count);
}

在这里插入图片描述
在这里插入图片描述


3. 求两个数二进制中不同位的个数

  题目: 求两个整数m和n的二进制序列中,有多少位不相同的?输入例子:1999 , 2299;输出例子:7。
  大家在没看过上面的算法思路时,来求解这道题目的思路一定是逐位进行比较的是否相等,效率太低。现在再思考一下这道题的解法,只需要两步:

  第1步:按位异或^计算两个数的相异处。

  第2步:用“ 消1计数法 ”统计二进制位中1的个数,即:二进制位相异处的个数。

  代码如下:

#include<stdio.h>
int sta_bit_num(int x, int y)
{
	int count = 0;
	int n = x ^ y;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	return count;
}
int main()
{
	int a = 0;
	int b = 0;
	int count = 0;
	printf("请输入要计算的两个数:");
	scanf("%d%d", &a, &b);
	count = sta_bit_num(a, b);
	printf("两个数二进制数中不同位的个数:%d\n", count);
}

在这里插入图片描述


4. 打印整数二进制的奇数位和偶数位

  题目: 获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列。
  该题先假设二进制序列中最低位为奇数位1,然后我们想办法从高位向低位打印奇数位、偶数位。这样就需要两个循环分别打印奇数位和偶数位了。

#include<stdio.h>
void print_odd(unsigned int n)
{
	int i = 0;
	printf("打印奇数位:\n");
	for (i = 30; i >= 0; i -= 2)
	{
		printf("%d ", (n >> i) & 1);
	}
	printf("\n");
}
void print_even(int n)
{
	int i = 0;
	printf("打印偶数位:\n");
	for (i = 31; i >= 1; i -= 2)
	{
		printf("%d ", (n >> i) & 1);
	}
	printf("\n");
}
int main()
{
	int n = 0;
	printf("请输入一个数:");
	scanf("%d", &n);
	print_odd(n);
	print_even(n);
	return 0;
}

在这里插入图片描述
在这里插入图片描述


5. 判断一个整数是不是2的n次方

  解题思路:由于整数 2n 的二进制序列只可能存在一个1。所以在求一个数是否为2 ^ n的整数时,只需要判断该数的二进制序列中只存在一个1就可以了。那这里不想也知道用消1计数法最为高效。代码如下:

#include<stdio.h>
int is_pow(int n)
{
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	return count;
	
}
int main()
{
	int n = 0;
	printf("请输入一个数:");
	scanf("%d", &n);
	if (is_pow(n) == 1)
	{
		printf("整数%d是2的n次方\n", n);
	}
	else
	{
		printf("整数%d不是2的n次方\n", n);
	}
	return 0;
}

在这里插入图片描述
在这里插入图片描述


6. 在成对元素的数组中找单身狗

6.1 题目1

  题目:{1,2,3,4,5,4,3,2,1}的一组整型数组中,只有一个数出现一次,其他数都是成对出现的,请找出那个只出现过一次的数字。
  解题思路:异或操作符有两个个性质x^x = 0x^0 = x,由于这一组数中只有一个数出现一次,其余数都是成对出现的,将所有数都异或在一起,就可以得到那个单身狗了。实现代码如下:

#include<stdio.h>
int find_once(int arr[], int sz)
{
	int i = 0;
	int tmp = 0;
	for (i = 0; i < sz; i++)
	{
		tmp = tmp ^ arr[i];
	}
	return tmp;
}
int main()
{
	int arr[] = { 1,2,3,4,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int result = find_once(arr, sz);
	printf("那个单身狗是:%d\n", result);
	return 0;
}

在这里插入图片描述


6.2 题目2

  题目:{1,2,3,4,5,4,3,2,1,6}的一组整型数组中,有两个数出现一次,其他数都是成对出现的,请找出那两个只出现过一次的数字。
  解题思路:异或操作符有两个个性质x^x = 0x^0 = x,由于这一组数中有两个数只出现一次,其余数都是成对出现的。可以找这两个数的区别,然后将这一组数分成两组,各占一个单身狗。最后分别将两组数异或在一起,就可以得到那两个单身狗了。实现代码如下:

#include<stdio.h>
void find_once(int arr[], int sz, int* pa, int* pb)
{
	//1.找两个单身狗的差异
	int diff = 0;
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		diff = diff ^ arr[i];
	}
	//2.确定差异的二进制位
	int pos = 0;
	for (i = 0; i < 32; i++)
	{
		if ((diff & 1) == 1)
		{
			pos = i;
			break;
		}
		diff = diff >> 1;
	}
	//3.按差异分组并将异或后的结果返回
	for (i = 0; i < sz; i++)
	{
		if ((arr[i] >> pos) & 1 == 1)
			*pa ^= arr[i];
		else
			*pb ^= arr[i];
	}
}
int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int a = 0;
	int b = 0;
	find_once(arr, sz, &a, &b);
	printf("找到了单身狗:%d,%d\n", a, b);
	return 0;
}

在这里插入图片描述


在这里插入图片描述

这份博客👍如果对你有帮助,给博主一个免费的点赞以示鼓励欢迎各位🔎点赞👍评论收藏⭐️,谢谢!!!
如果有什么疑问或不同的见解,欢迎评论区留言欧👀。


http://www.kler.cn/news/319607.html

相关文章:

  • python库 | lxml库
  • Python_yield
  • 【项目实战】如何在项目中自定义错误码
  • VisualStudio编译时出现无法启动mt.exe
  • C++之spring
  • Codeforces Round 973 (Div. 2) C. Password Cracking
  • 抓取股票数据
  • 28岁打算转行靠谱么,这个年龄转行,有什么适合的行业么?
  • opencv滤波算法总结
  • Linux挂载命令
  • Docker 的安装部署与基本使用
  • Apache CVE-2021-41773 漏洞攻略
  • What is the new in C#11?
  • 验收测试:从需求到交付的全程把控!
  • BERT训练环节(代码实现)
  • 通过docker启动ElasticSearch后为ElasticSearch设置用户和密码
  • 分享课程:VUE数据可视化教程
  • vant_UI的选择时间小组件封装
  • excel VBA进行间比法设计
  • 运行python程序
  • 初识前端监控
  • C++如何进阶? -- 整理一些学习资料
  • 基于stm32物联网身体健康检测系统
  • LeetCode 909. 蛇梯棋
  • nlohmann json:读写json文件
  • c++优先级队列自定义排序实现方式
  • SDK3(note上)
  • NLP 文本分类任务核心梳理
  • Selenium点击元素的方法
  • 【深入学习Redis丨第六篇】Redis哨兵模式与操作详解