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

130 阅读6分钟

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

[TOC]

一、位运算符总结概述

位运算全面总结,关于位运算看这篇就够了

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、判断奇偶数

#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、交换两个整型变量的值(异或法)

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

三、实战例题

例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的个数

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

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

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

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

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