【C语言刷题】——初识位操作符

news2024/10/5 16:26:31

【C语言刷题】——初识位操作符

    • 位操作符介绍
    • 题一、 不创建临时变量(第三个变量),实现两个数的交换
      • (1)法一
      • (2)法二
    • 题二、 求一个数存储在内存中的二进制中“一”的个数
      • (1)法一
      • (2)法二
      • (3)法三
    • 题三、 单身狗1
      • (1)法一
      • (2)法二
    • 题四、 单身狗二
      • (1)法一
      • (2)法二
      • (3)法三

位操作符介绍

位操作符有:

<<      //左移操作符
>>      //右移操作符
&       //按位与
|       //按位或
^       //按位异或
~       //按位取反

  注:他们的操作数必须是整数。

  更多关于位操作符介绍请看这篇文章:【C语言】——详解操作符(上)

题一、 不创建临时变量(第三个变量),实现两个数的交换

(1)法一

  
参考代码:

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 10;
	a = a + b;
	b = a - b;
	a = a - b;
	printf("交换后a = %d b = %d\n", a, b);
	return 0;
}

  
代码讲解:

  相信这段代码大家都能看懂,这里我就不多解释了。
  
  但遗憾的是这个方法有一点问题:当 a 和 b 的值很大(但都不超过 i n t int int 的存储空间)时,他们相加会超过 int 的存储空间,导致丢失数据。
  
  那有什么更好的办法吗?有的,请看法二。

  

(2)法二

  
参考代码:

#include<stdio.h>

int main()
{
	int a = 0;
	int b = 10;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("交换后a = %d b = %d", a, b);
	return 0;
}

  
代码讲解:

首先,我们需掌握几个知识点:

  • 一个数与他自身异或为 0 0 0 ,即: a a a ^ a a a == 0 0 0
  • 任何数与 0 0 0 异或结果都为其本身,即: a a a ^ 0 0 0 == 0 0 0
  • 异或运算满足交换律,即: a a a ^ a a a ^ b b b == a a a ^ b b b ^ a a a

  第八行代码:b = a ^ b;,由于第七行代码:a = a ^ b; ,此时a = a ^ b;,即b = a ^ b ^ b,得b = a ^ 0 ;得 b = a

  同理,第九行代码:a = a ^ b;,由于第七行代码:a = a ^ b; 和第八行代码结果b = a,即:a = a ^ b ^ a,得 a = b

  我们可以这样来理解:我们把第七行代码a = a ^ b;当成是一把钥匙,碰到 a 得 b碰到 b 得 a

  

题二、 求一个数存储在内存中的二进制中“一”的个数

  

(1)法一

  
参考代码:

#include <stdio.h>

int main()
{
	int num = 0;
	scanf("%d", &num);
	int count = 0;//计数
	while (num)
	{
		if (num % 2 == 1)
			count++;
		num = num / 2;
	}
	printf("二进制中1的个数 = %d\n", count);
	return 0;
}

  
代码讲解:

  其实,该法的解题思路与在十进制中打印每一位是思路相同。在十进制中,要想获得每一位,我们的方法是:先余十,再除十,不断循环。这里,也是一样的只是因为是二进制改为先余二,再除而,不断循环,遇到结果为 1 ,则计数器加一。
  
  但是这个方法有点问题:它只能处理正数的情况,如果是负数,他就没办法了。为什么?因为:if(num % 2 == 1),负数余二永远不可能为 1,但num = num / 2;语句正常执行,输入复数的结果永远是 0。

  

(2)法二

  
参考代码:

#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if (n & 1)
		{
			count++;
		}
		n = n >> 1;
	}
	printf("%d", count);
	return 0;
}

  
代码讲解:

  法二的思路是:给这个数的每一位都与上 1 (不断右移),因为与的逻辑是:有 0 为 0,全 1 为 1。当运算结果为 0 ,表示该位为 0, 结果为 1 ,该位为 1
  
图解(以5为例):
在这里插入图片描述
结果为1,计数器加一
  
5右移一位:
在这里插入图片描述
结果为0,计数器不变
  
5再右移
在这里插入图片描述
结果为1,计数器加一
  
接着不断右移,一共32次,因为后面的结果都是0,变不再一一赘述。
  
  但该方法一定要循环32次,有没有效率更高的方法呢?

  

(3)法三

  
参考代码:

#include<stdio.h>

int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	printf("%d", count);
	return 0;
}

  
代码讲解:

该方法的核心:这个数本身与他自身减一与(&) 运算,不断循环
  

n = n & (n - 1);

或许大家一脸疑惑,别急,直接上图!

以15为例:
循环一次
在这里插入图片描述

循环两次:
在这里插入图片描述

循环三次:
在这里插入图片描述

循环四次:
在这里插入图片描述

  大家发现没有,每循环一次就会把最右边的 1 给消去,当最终把所有 1 消去变成零,循环结束,而我们只需要计算循环了多少次就能知道该数有几个 1 。

  

题三、 单身狗1

  
题目:

  在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。
  
例如:

  数组中有:1 2 3 4 5 1 2 3 4,只有5出现一次,其他数字都出现2次,找出5

  

(1)法一

  
参考代码:

#include<stdio.h>

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		int flag = 0;
		int n = 0;

			for (int j = 0; j < sz; j++)
			{

				if (i == j)
					continue;
				n = arr[i] ^ arr[j];
				if (n == 0)
				{
					flag = 1;
					break;
				}
			}

		if (flag == 0)
			printf("单身狗是:%d\n", arr[i]);
	}
	return 0;
}

  
代码讲解:

  该方法想必大家都很容易想到,逻辑也很简单:遍历数组中的每一个数,每个数再遍历一遍除自身外的整个数组,遇到与自身一样的数就说明自己不是单身狗。
  这里就不再过多解释

  

(2)法二

  
参考代码:

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int a = 0;
	for (int i = 0; i < sz ; i++)
	{
		a = arr[i] ^ a;
	}
	printf("单身狗是:%d\n", a);
	return 0;
}

  
解题思路:

这里再带大家重温一下按位异或的相关知识:
(1)一个数与他自身异或为 0 0 0 ,即: a a a ^ a a a == 0 0 0
(2)任何数与 0 0 0 异或结果都为其本身,即: a a a ^ 0 0 0 == 0 0 0

那么综合运用起来就是这题的解法啦

将数组元素全部异或
  
a = 1 ^ 2 ^ 3 ^ 4 ^ 5 ^ 1 ^ 2 ^ 3 ^ 4
a = 1 ^ 1 ^ 2 ^ 2 ^ 3 ^ 3 ^ 4 ^ 4 ^ 5
a = 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 0 ^ 5
a = 5

题四、 单身狗二

  
题目:

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

  
例如:

有数组的元素是:1,2,3,4,5,1,2,3,4,6

只有5和6只出现1次,要找出5和6.

  

(1)法一

参考代码:

#include<stdio.h>

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 1, 2, 3, 4,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int count = 0;
	for (int i = 0; i < sz; i++)
	{
		int flag = 0;
		int n = 0;

		for (int j = 0; j < sz; j++)
		{
			if (i == j)
				continue;
			n = arr[i] ^ arr[j];
			if (n == 0)
			{
				flag = 1;
			}
			
		}
		if (0 == flag)
		{
			printf("单身狗是:%d\n", arr[i]);
			count++;
		}
		if (2 == count)
			break;
	}
	return 0;
}

  
代码讲解:

  这段代码的思路与上一题单身狗一的法一思路是相同的,
  
  即依次取出数组中的每个元素,让他与数组中剩下的元素比较,当遍历完整个数组依然没找到相同的数时,说明他是其中一个单身狗,计数器 +1 ,并打印;当计数器为二时,说明已找到全部单身狗,退出循环。

  

(2)法二

参考代码:

#include<stdio.h>

int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		int j = 0;
		int last_0 = 0;
		int last_1 = 0;
		for (j = 0; j < sz; j++)
		{
			if ((arr[j] & (1 << i)) == 0)
			{
				last_0 ^= arr[j];
			}
			else if ((arr[j] & (1 << i)) != 1)
			{
				last_1 ^= arr[j];
			}
		}
		if(last_0 != 0 && last_1 != 0)
		{
			printf("%d %d", last_0, last_1);
			break;
		}
	}
	return 0;
}

  
代码讲解:

  做了上面的单身狗,我们想这题应该也可以用异或分方法来解,
  
  我们想到,如果将所有元素直接异或,肯定是无法直接找出两只单身狗,这时我们可以想到先将他们分组,让每一组都只有一只单身狗,再将两组全部异或就行了。那么怎么分组呢?
  
  我们看到因为数组两两元素相同,加上两只单身狗,所以数组总数一定是偶数,这时我们可以依次遍历数组中所有元素的二进制位数,将该位为 1 和为 0 的元素分成两组,分完组后,若两组的元素个数都为奇数(相同的数该位一定相同,一定被分到同一组,剩下一只单身狗,为奇数),则成功将两只单身狗分开,再分别异或两组中的所有元素就能找出两周单身狗啦,
  
  如果分完组两边都是偶数,则比较二进制下一位,直到分出奇数组。

  

(3)法三

参考代码:

#include<stdio.h>

void findTwoNum(int arr[], int n, int * pnum1, int * pnum2)
{
 int i;
 int sum = 0;for (i = 0; i < 9; i++)
 {
  sum ^= arr[i];
 } //先找到两个数互相异或的结果int pos;
 for (i = 0; i < 32; i++)
 {
  if (sum & 1 << i)
  {
   pos = i;
   break;
  }
 } //再找到有分歧的一位。在这一位上,两个数一定是一个1一个0*pnum1 = *pnum2 = 0;
 for (i = 0; i < 10; i++)
 {
  if (arr[i] & 1 << pos)
  {
   *pnum1 ^= arr[i]; //这一位是1的,放在数1里
  }
  else
  {
   *pnum2 ^= arr[i]; //这一位是0的,放在数2里
  }
 }
}

  
代码讲解:

  法二虽然用到了异或操作,但分起组来太过麻烦,效率并不高,有没有什么方法能实现快速分组呢?答案当然是有的。

  我们知道:两个相同的数异或为 0,将数组中的所有元素异或起来,得到的结果就是两只单身狗异或的结果。
  
  
  
  这时我们可能就要问了,那得到这个结果有什么用呢?肯定不止唯二这两个数异或才得出这个结果,其他两个数异或也有可能得到这个结果。

  确实如此,但我们别忘了异或的特点:相同为 0,相异为 1。我们只需要找到异或的结果的其中一个为“ 1 ”的位数,这说明在这个位,其中一只单身狗是0,另一只为1。

  这时,我们只需要将数组中的元素分两组:一组的元素在该位的值为 0,另一组该位值为 1,再将两组的所有元素分别异或起来,自然就得到两只单身狗啦。怎么样,是不是很巧妙呢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1510671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

论文阅读:FCB-SwinV2 Transformer for Polyp Segmentation

这是对FCBFormer的改进&#xff0c;我的关于FCBFormer的论文阅读笔记&#xff1a;论文阅读FCN-Transformer Feature Fusion for PolypSegmentation-CSDN博客 1&#xff0c;整体结构 依然是一个双分支结构&#xff0c;总体结构如下&#xff1a; 其中一个是全卷积分支&#xff…

浏览器缓存 四种缓存分类 两种缓存类型

浏览器缓存 本文主要包含以下内容&#xff1a; 什么是浏览器缓存按照缓存位置分类 Service WorkerMemory CacheDisk CachePush Cache 按照缓存类型分类 强制缓存协商缓存 缓存读取规则浏览器行为 什么是浏览器缓存 在正式开始讲解浏览器缓存之前&#xff0c;我们先来回顾一…

【保姆级教程】JDK安装与环境变量配置

文章目录 第一步&#xff1a;下载JDK&#xff08;以1.8为例&#xff09;第二步&#xff1a;安装第三步&#xff1a;找到默认安装目录第四步&#xff1a;配置环境变量&#xff08;win10为例&#xff09; 大家可能会遇到的疑问&#xff1a;一个电脑可以安装多个版本的jdk没有问题…

头脑风暴法是什么?10个值得推荐的头脑风暴模板!

身处职场的你&#xff0c;想必对头脑风暴这个术语并不陌生&#xff0c;它可能是某个同事或者领导的口头禅&#xff0c;每当遇到需要给出方案的场景&#xff0c;头脑风暴或者“脑暴”就会从他们嘴里脱口而出&#xff0c;但你真的了解&#xff0c;头脑风暴是什么意思吗&#xff1…

力扣刷题日志-Day2 (力扣151、43、14)

151. 反转字符串中的单词 给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开 思路&#xff1a;根据题目大意&#xff0c;空格之间的就是一个单词&#xff0c;所以我们需要利用…

Sui与数据平台ZettaBlock达成合作,为其公测提供数据

Sui一向以闪电般的速度、无限水平扩展著称&#xff0c;现已迅速成为DeFi活动的重要场所。近期&#xff0c;数据平台ZettaBlock宣布在其开创性的Web3数据平台发布中&#xff0c;选择Sui作为基础集成合作伙伴之一。在ZettaBlock的开放测试版发布之际&#xff0c;构建者和开发者将…

【JAVA】HashMap扩容性能影响及优化策略

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 在软件开发中&#xff0c;HashMap是一种常用的数据结构&#xff0c;但在处理大量数据时&#xff0c;其扩容…

11、设计模式之享元模式(Flyweight)

一、什么是享元模式 享元模式是一种结构型的设计模式。它的主要目的是通过共享对象来减少系统种对象的数量&#xff0c;其本质就是缓存共享对象&#xff0c;降低内存消耗。 享元模式将需要重复使用的对象分为两个部分&#xff1a;内部状态和外部状态。 内部状态是不会变化的&…

进电子厂了,感触颇多...

作者&#xff1a;三哥 个人网站&#xff1a;https://j3code.cn 本文已收录到语雀&#xff1a;https://www.yuque.com/j3code/me-public-note/lpgzm6y2nv9iw8ec 是的&#xff0c;真进电子厂了&#xff0c;但主人公不是我。 虽然我不是主人公&#xff0c;但是我经历的过程是和主…

【2024-03-12】设计模式之模板模式的理解

实际应用场景&#xff1a;制作月饼 过程描述&#xff1a; 一开始&#xff0c;由人工制作月饼&#xff0c; 第一个&#xff1a;根据脑子里面月饼的形状&#xff0c;先涅出月饼的形状&#xff0c;然后放入面粉和馅料把开口合并起来。 第二个&#xff1a;根据脑子里面月饼的形状&…

短剧小程序系统:打造沉浸式短剧体验,开启短剧新纪元

随着移动互联网的迅猛发展&#xff0c;短剧作为一种新兴的内容形式&#xff0c;正逐渐受到广大用户的喜爱和追捧。为了满足用户对短剧内容的需求和观看体验的提升&#xff0c;短剧小程序系统应运而生&#xff0c;为用户带来了更加便捷、沉浸式的短剧观看体验。 短剧小程序系统…

如何在Linux本地搭建Tale网站并实现无公网ip远程访问

文章目录 前言1. Tale网站搭建1.1 检查本地环境1.2 部署Tale个人博客系统1.3 启动Tale服务1.4 访问博客地址 2. Linux安装Cpolar内网穿透3. 创建Tale博客公网地址4. 使用公网地址访问Tale 前言 今天给大家带来一款基于 Java 语言的轻量级博客开源项目——Tale&#xff0c;Tale…

【Python】科研代码学习:八 FineTune PretrainedModel (用 trainer,用 script);LLM文本生成

【Python】科研代码学习&#xff1a;八 FineTune PretrainedModel [用 trainer&#xff0c;用 script] LLM文本生成 自己整理的 HF 库的核心关系图用 trainer 来微调一个预训练模型用 script 来做训练任务使用 LLM 做生成任务可能犯的错误&#xff0c;以及解决措施 自己整理的 …

8块硬盘故障的存储异常恢复案例一则

关键词 华为存储、硬盘域、LUN热备冗余、重构、预拷贝 oracle rac、多路径 There are many things that can not be broken&#xff01; 如果觉得本文对你有帮助&#xff0c;欢迎点赞、收藏、评论&#xff01; 一、问题现象 近期遇到的一个案例&#xff0c;现象是一套oracl…

PXI8540高速数据采集卡

XI高速数据采集卡&#xff0c;PXI8540卡是一种基于PXI总线的模块化仪器&#xff0c;可使用PXI系统&#xff0c;在一个机箱内实现一个综合的测试系统&#xff0c;构成实验室、产品质量检测中心等各种领域的数据采集、波形分析和处理系统。也可构成工业生产过程监控系统。它的主要…

ThreeWayBranch 优化阅读笔记

1. 优化目的 通过重排三分支的 BB 块减少比较指令的执行次数 代码路径: bolt/lib/Passes/ThreeWayBranch.cpp2. 效果 优化前&#xff1a; 注&#xff1a; 黄色数字表示BB块编号&#xff0c; 紫色表示该分支跳转的次数&#xff0c;绿色是代码里BB块的变量名 ThreeWayBranc…

P6327 区间加区间 sin 和 (线段树+数学)

传送门https://www.luogu.com.cn/problem/P6327 比较板子的一题&#xff0c;主要考察公式 //sin(ax)sinxcosasinacosx //cos(ax)cosacosx-sinasinx 直接贴代码吧 // Problem: // P6327 区间加区间 sin 和 // // Contest: Luogu // URL: https://www.luogu.com.cn/pr…

@Conditional注解详解

目录 一、Conditional注解作用 二、Conditional源码解析 2.1 Conditional源码 2.2 Condition源码 三、Conditional案例 3.1 Conditional作用在类上案例 3.1.1 配置文件 3.1.2 Condition实现类 3.1.3 Bean内容类 3.1.4 Config类 3.1.5 Controller类 3.1.6 测试结果 3…

ChatGPT GPT4科研应用、数据分析与机器学习、论文高效写作、AI绘图技术

原文链接&#xff1a;ChatGPT GPT4科研应用、数据分析与机器学习、论文高效写作、AI绘图技术https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247596849&idx3&sn111d68286f9752008bca95a5ec575bb3&chksmfa823ad6cdf5b3c0c446eceb5cf29cccc3161d746bdd9f2…