草庐IT

蓝桥杯算法竞赛系列第一章——位运算的奇巧淫技及其实战

安然无虞 2023-08-26 原文

遇见蓝桥遇见你,不负代码不负卿!

第二章”递归“已将更新咯,欢迎铁汁们点评!蓝桥杯算法竞赛系列第二章——深入理解重难点之递归(上)_安然无虞的博客-CSDN博客

目录

一、位运算符总结概述

1、按位与 &

2、按位或 |

3、按位取反 ~

4、按位异或 ^

5、补充了解移位运算

1.左移<<

2.右移>>

3.无符号右移>>>

6、补充了解原码反码补码

1.原码

2.反码

3.补码

二、蓝桥云课:位运算的奇巧淫技

1、判断奇偶数

解题思路:

代码执行

2、判断二进制位是1还是0(两种方法)

方法一解题思路

 代码执行

方法二解题思路

代码执行

3、交换两个整型变量的值(异或法)

解题思路

 代码执行

4、不用判断语句,求整数的绝对值

解题思路

代码执行

三、实战例题

例1、如何找数组中唯一成对的那个数

解题思路

代码执行

例2、找出落单的那个数

解题思路

代码执行

例3、二进制中1的个数

方法1解题思路

代码执行

方法2解题思路

代码执行

方法3解题思路

代码执行

例4、是不是2的整数次方

解题思路

代码执行

例5、将整数的奇偶位互换

解题思路

思考题:出现K次与出现一次

四、蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!

五、笔者请求


推荐给老铁们两个学习网站:

面试利器&算法学习:牛客网

风趣幽默的学人工智能:人工智能学习​​​​​​​

【前言】:笔者是根据蓝桥杯官方发布的视频整理出的笔记,博文是由笔记二次整理而来,具有较强的针对性,打算参赛的小伙伴跟着笔者一起行动起来吧!为了方便更多的铁汁们,代码示例中笔者采用的是C语言编写。

【友情提示】:在接下来的两个半月中,博主将会持续推出两个系列的博文,一是零基础搞定C语言,二是蓝桥杯算法竞赛,包括一些刷题感悟与总结哦,当然后面还会有往年的蓝桥杯真题总结,喜欢的铁汁们可以关注笔者哦。 

一、位运算符总结概述

1、按位与 &

如果两个二进制位都为1,则结果是1,否则是0

2、按位或 |

如果两个二进制位都为0,则结果是0,否则是1

3、按位取反 ~

该位为0,则变为1,否则变为0

4、按位异或 ^

如果两个数字的二进制位相同,则结果为0,相异则结果为1

5、补充了解移位运算

实际运用当中可以根据情况用左移/右移做快速的乘除法,这样效率会高很多。

1.左移<<

最左侧不要了,最右侧直接补0;(左移相当于乘法,如左移1位,相当于乘以2的1次方)

2.右移>>

右移相当于除法...

因为C语言中没有特别引入无符号右移,所以C语言中的右移分成算术右移和逻辑右移两种。

算术右移就是最右侧位不要了,最左侧直接补符号位(大多数机器上采用的是算术右移)

所以应用C语言中的右移需要格外注意负数的情况。

逻辑右移就是最右侧不要了,最左侧直接补0

注意:对于int 类型的数据,移位超出32位时,需要模上32,比如1 << 35 == 1 << 3;

           那么对于long类型来说,超出时需要模上64(模-->求余)

3.无符号右移>>>

引入Java中的无符号右移,它和左移的用法一直,也就是逻辑右移,最右侧不要了,最左侧补0

6、补充了解原码反码补码

1.原码

直接将这个数字按照正负数的形式翻译成二进制即可

2.反码

原码的符号位不变,其他位按位取反即可

3.补码

反码+1即可得到补码

注意:正数和无符号数的原码、反码、补码都相同,只有求负数的反码、补码才采用上面的计算

    int a = 20;  //整型a是4个字节,32位

	// 00000000 00000000 00000000 00010100 - 原码
	// 00000000 00000000 00000000 00010100 - 反码
	// 00000000 00000000 00000000 00010100 - 补码

	int b = -10;

	// 10000000 00000000 00000000 00001010 - 原码
	// 11111111 11111111 11111111 11110101 - 反码
	// 11111111 11111111 11111111 11110110 - 补码

声明:对于数据的存储只简单介绍到这里,详细介绍将放在后面零基础搞定C语言系列

二、蓝桥云课:位运算的奇巧淫技

1、判断奇偶数

解题思路:

拿int型举例,int占4个字节,也就是32位,对于整型来说,数据存放在内存当中存放的是其补码形式,由于第一位(注意上面图片中第1位的位置)前面的数都是2的1及以上次方,只有当第一位是1时为奇数,是0时为偶数,大家先理解一下上面这句话,明白了以后,如果我们想要判断一个整数的奇偶性,只需要和1进行按位&运算即可,由于1中除第1位以外每一位都是0,又因为任何数中的每一位与1中的0位做&运算,与0位做&运算的位结果都是0,所以偶数和1进行&运算是0,奇数和1进行&运算是1

代码执行

#include<stdio.h>
int main()
{
	printf("判断奇偶数:\n");
	int num = 12;
	if ((num & 1) == 0)
	{
		printf("%d是偶数\n",num);
	}
	else
	{
		printf("%d是奇数\n", num);
	}
	return 0;
}

2、判断二进制位是1还是0(两种方法)

例:int x = 86; // 定义一个整型变量x, 并将其初始化为86

       现需要判断第5位是1还是0, 该如何操作?

方法一解题思路

老样子,还是用整数1执行操作,由于是判断x的第五位是1还是0,所以取整数1,让1左移(5 - 1)位,也就是左移4位,然后直接和x进行按位&操作,最后再右移4位至该数二进制第一位,若第1位是0,则第5位上的二进制数是0,否则是1

 代码执行

#include<stdio.h>
int main()
{
	int x = 86;
	printf("判断整数86的二进制第5位是1还是0:\n");
	//运算符的使用有优先级,我们不必特意去记这个,可能存在歧义的时候,就加上括号,这样既保险又省心
	if ((((1 << 4) & x) >> 4) == 0)
	{
		putchar('0');
	}
	else
	{
		putchar('1');
	}
	return 0;
}

方法二解题思路

该方法较方法一就简单多了,它利用了“判断奇偶数”的解题思想,首先,让x右移(5 - 1)位,即右移4位,然后再和1相&,如果结果是0,则第5位上的二进制数是0,否则是1。

代码执行

#include<stdio.h>
int main()
{
	int x = 86;
	printf("判断整数86的二进制第5位是1还是0:\n");
	if (((x >> 4) & 1) == 0)
	{
		putchar('0');
	}
	else
	{
		putchar('1');
	}
	return 0;
}

3、交换两个整型变量的值(异或法)

解题思路

补充:异或,可理解为不进位加法,如1+1 = 0, 0 + 0 = 0, 1 + 0 = 1

异或:如果两个数字的二进制位相同,则结果为0,相异则结果为1.

异或的性质:

1.交换律:可任意交换运算因子的位置,结果不变;

   如:a ^ b ^ c = b ^ a ^ c;

2.结合律:即(a ^ b) ^ c == a ^ ( b ^ c) ;

3.对于任何数x, 都有x ^ x = 0, x ^ 0 = x,同自己求异或为0,同0求异或为自己

4.自反性:A ^ B ^ B = A ^ 0 = A, 连续和同一个因子做异或运算,最终结果为自己

说了这么多,那么位运算中的异或到底跟“交换两个整型变量的值”这个题目有什么联系呢,请听笔者向铁汁们慢慢道来...

例题:int A = 10,  int B = 20,  在不引入第3个变量的情况下,交换两个变量的值(除了采用异或法,用加减法也可以,这个将在后面的零基础搞定C语言中详细介绍)

 代码执行

#include<stdio.h>
int main()
{
	int A = 10;
	int B = 20;
	printf("交换前A = %d B = %d\n", A, B);
	A = A ^ B;
	B = A ^ B;
	A = A ^ B;
	printf("交换后A = %d B = %d\n", A, B);
	return 0;
}

4、不用判断语句,求整数的绝对值

解题思路

如果上面的表述看不懂也没有关系,表达式已经给出来了,你只需要知道当遇到不用判断语句求整数的绝对值用它即可。

代码执行

本题采用Java语言编写

public class Test10_16 {
	public static void main(String[] args) {

		System.out.println("不用判断语句,求整数的绝对值");
		int num5 = -100;
		System.out.println("num5的绝对值是:" + ((num5 ^ (num5 >> 31)) + (num5 >>> 31)));
	}
}

三、实战例题

例1、如何找数组中唯一成对的那个数

1-10这10个数放在含有11个元素的数组中,只有唯一一个元素重复,其他均只出现一次,要求每个数组元素只能够被访问一次,请设计一个算法,将它找出来,不用辅助存储空间,能否设计一个算法实现?

解题思路

想要计算出本题,需要我们掌握异或的性质,思路:定义一个数x,并将它初始化成0,将它和数组中的11个数异或,再和1-10这10个自然数异或,最终的结果就是那个数。

代码执行

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,2,6,4,7,5,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int x = 0;//之所以将x初始化为0,因为0与任何数异或都是它自己
	for (int i = 0; i < sz; i++)
	{
		x = x ^ arr[i];
	}
	for (int i = 1; i <= sz - 1; i++)
	{
		x = x ^ i;
	}
	printf("%d\n", x);	
	return 0;
}

例2、找出落单的那个数

一个数组中除了某一个元素中之外,其他的元素都出现了两次,请写程序找出这份只出现一次的数字

解题思路

这道题比第一道题还要简单,直接异或即可

代码执行

#include<stdio.h>
int main()
{
	int arr[] = { 1,1,2,3,3,4,4,5,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int x = 0;
	for (int i = 0; i < sz; i++)
	{
		x = x ^ arr[i];
	}
	printf("%d\n", x);
	return 0;
}

例3、二进制中1的个数

输入一个整数,输出该数二进制表示中1的个数

如9的二进制表示为00000000 00000000 00000000 00001001,有两个1

方法1解题思路

笨方法,把1给数出来-->让1动,那个整数一直不动,利用32次循环,每一次都是将相应位上的数字进行按位&操作

代码执行

#include<stdio.h>
int main()
{
	int num = 9;
	int count = 0;//用于计数
	for (int i = 0; i < 32; i++)
	{
        //一定要注意写法,虽然简单,但是容易出错
		if ((num & (1 << i)) == (1 << i))-----注意写成if((num & (1 << i) == 1)就错了
		{
			count++;
		}
	}
	printf("%d\n", count);
	return 0;
}

方法2解题思路

其实就是方法1的一个变形,保持1的位置不动,让那个整数不断右移

代码执行

#include<stdio.h>
int main()
{
	int num = 9;
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if (((num >> i) & 1) == 1)
		{
			count++;
		}
	}
	printf("%d\n", count);
	return 0;
}

方法3解题思路

技多不压身,铁汁们最好将这三种方法全部掌握,保不准在之后的题目中会用到。

利用循环,当这个整数变为0时循环结束,循环体中执行num = (num - 1) & num 这个操作

 可能大家一开始的时候想不到这个解法,笔者也是,我一开始看的时候也很蒙,但是当你看完了之后会有种醍醐灌顶的感觉,说明你理解了原来还可以这样。虽然之前我们可能想不到,但是既然现在遇到了,那就要记住它,以后再出现的时候就要会运用了,一起加油吧铁汁们。

代码执行

#include<stdio.h>
int main()
{
	int num = 9;
	int count = 0;
	while (num != 0)
	{
		num = (num - 1) & num;
		count++;
	}
	printf("%d\n", count);
	return 0;
}

例4、是不是2的整数次方

同一条语句判断一个整数是不是2的整数次方

解题思路

判断一个整数是不是2的整数次方,也就是这个整数的二进制中只有一个1

代码执行

看,这里就用到了上一题的方法3,所以我们该不该记住呢...

#include<stdio.h>
int main()
{
	int num = 9;
	if (((num - 1) & num) == 0)
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
	return 0;
}

当你刷题的时候刷到了这题请注意要添加一个条件哦,就是这个num一定是大于0的,因为它是2的幂。 

例5、将整数的奇偶位互换

注意:用1做&运算其实就是保留,用0做&运算其实就是消除

解题思路

#include<stdio.h>
int main()
{
	int num = 9; //0000....00001001
	int a = 0xaaaaaaaa;  //1010....10101010
	int b = 0x55555555;  //0101....01010101
	int c = num & a;  //目的是保留num的偶数位
	int d = num & b;  //目的是保留num的奇数位

	printf("%d\n", ((c >> 1) ^ (d << 1)));

	return 0;
}

思考题:出现K次与出现一次

题目描述:数组中只有一个数出现了1次,其他数都出现了K次,请输出这出现一次的数,需要用位运算,不可以采用暴力求解法。

注意:以后笔者的博文中可能每篇都会有下一道思考题留给铁汁们做,等到笔者下次发表该系列博文的时候会公布解题思路权当复习了。

四、蓝桥结语:遇见蓝桥遇见你,不负代码不负卿!

大学可以说是人生中最美好的阶段,我们虽然有压力,但是相比以后工作、生活而言就算不上什么了,对于身处IT浪潮中的我们而言,愿大家不负韶华,珍惜机会,丰富经历,学有所成。

五、笔者请求

铁汁们,笔者的博文都是由纸质和电子笔记的二次加工而来的,花费了比较多的心力,感觉写的还可以的话,给俺来个点赞,收藏加关注呗,你们的支持就是笔者最大的动力

第二章“递归”已经更新咯,欢迎铁汁们指点。蓝桥杯算法竞赛系列第二章——深入理解重难点之递归(上)_安然无虞的博客-CSDN博客

准备算法竞赛,或者是想提升基础算法能力的铁汁们都可以订阅哦,免费的啦,周周都有更新。

 https://blog.csdn.net/weixin_57544072/category_11418082.html?spm=1001.2014.3001.5482

有关蓝桥杯算法竞赛系列第一章——位运算的奇巧淫技及其实战的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  3. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  4. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  5. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  6. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  7. 阿里云RDS——产品系列概述 - 2

    基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于

  8. ruby - 带括号和 splat 运算符的并行赋值 - 2

    我明白了:x,(y,z)=1,*[2,3]x#=>1y#=>2z#=>nil我想知道为什么z的值为nil。 最佳答案 x,(y,z)=1,*[2,3]右侧的splat*是内联扩展的,所以它等同于:x,(y,z)=1,2,3左边带括号的列表被视为嵌套赋值,所以它等价于:x=1y,z=23被丢弃,而z被分配给nil。 关于ruby-带括号和splat运算符的并行赋值,我们在StackOverflow上找到一个类似的问题: https://stackoverflow

  9. ruby-on-rails - CarrierWave - PDF - 只选择第一页 - 2

    我的Rails应用程序中安装了carrierwave。但是,当用户上传多页pdf时,我只希望应用程序获取文档中的第一页并将其转换为jpeg。这可能吗?用什么命令?这是我的uploader。#encoding:utf-8classImageUploader[200,300]##defscale(width,height)##dosomething#end#Createdifferentversionsofyouruploadedfiles:version:thumbdoprocess:resize_to_fill=>[150,210]process:convert=>:jpgdefful

  10. ruby - 如何跳过 CSV 文件的第一行并将第二行作为标题 - 2

    有没有办法跳过CSV文件的第一行,让第二行作为标题?我有一个CSV文件,第一行是日期,第二行是标题,所以我需要能够在遍历它时跳过第一行。我尝试使用slice但它会将CSV转换为数组,我真的很想将其读取为CSV,以便我可以利用header。 最佳答案 根据您的数据,您可以使用另一种方法和skip_lines-option此示例跳过所有以#开头的行require'csv'CSV.parse(DATA.read,:col_sep=>';',:headers=>true,:skip_lines=>/^#/#Markcomments!)do|

随机推荐