算法基础:位运算

目录

位运算和进制基础

位运算符

&(与),|(或),^(异或,~(非/取反)

<<和>>和>>>

位运算在算法中的应用

判断奇数偶数

获取指定二进制位是1还是0

交换两个整数变量的值

例题

1.如何找出数组中唯一成对的那个数(不开辅助空间)

2.找出落单的数

3.找到某个数二进制中1的个数

4.判断一个数是不是2的整数次

5.将数进行奇数位偶数位互换

​编辑

位运算和进制基础

位运算符

&(与),|(或),^(异或,~(非/取反)

&(与):运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1,说白了就是全为1&才为1,有一个是0结果就是0

举个例子:4&5  0000 0100&0000 0101=0000 0 100 所以4&5=4

|(或):运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1说白了就是 有一个是1结果就是1,全是0结果才为0

举个例子:4|5 0000 0100|0000 0101=0000 0101 4|5=5

^(异或):运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;就是如果相同为0,不同为1

~(取反):运算规则:~1=0; ~0=1;

再说一说原码,反码和补码(最终在内存中以补码存放)

一个字节有8个bit位,计算机用最高位是符号位,正数为0,负数为1

一个 byte 有 8bit,最大值是 0 1 1 1 1 1 1 1 (+127),最小值是 1 1 1 1 1 1 1 1 (-128)

十进制数据的二进制表现形式就是原码,原码最左边的一个数字就是符号位,0为正,1为负。

举个例子:15的原码是0000 1111

负数反码是原码符号位不变,其余位取反得到的,补码 = 反码+1

需要注意的是正数的原码=反码=补码,负数的反码 = 原码的符号位不变其余按位取反,负数的补码 = 反码+1

一个整型类型是4个字节,一个字节8个bit,所以一个整型类型32位

我们举出一个原码,反码,补码的例子

15

原码:0000 0000 0000 0000 0000 0000 0000 1111

反码:0000 0000 0000 0000 0000 0000 0000 1111

补码:0000 0000 0000 0000 0000 0000 0000 1111

-15

原码:1000 0000 0000 0000 0000 0000 0000 1111

反码:1111 1111 1111 1111 1111 1111 1111 0000(第一位的1不变,其余按位取反)

补码:1111 1111 1111 1111 1111 1111 1111 0001(反码+1)

<<和>>和>>>

<<是将二进制向左移动,补充0

>>是将二进制向右移动,补充最高位

>>>将二进制向右移动,补充0

15<<1

0000 0000 0000 0000 0000 0000 0000 1111

0000 0000 0000 0000 0000 0000 0001 1110

-15<<1(符号位不变)

1000 0000 0000 0000 0000 0000 0000 1111

1000 0000 0000 0000 0000 0000 0001 1110

15>>1

0000 0000 0000 0000 0000 0000 0000 1111

0000 0000 0000 0000 0000 0000 0000 0111(补充符号位)

-15>>1

1000 0000 0000 0000 0000 0000 0000 1111

1100 0000 0000 0000 0000 0000 0000 0111

位运算在算法中的应用

判断奇数偶数

#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	int b = a & 1;
	cout << "b: " << b << endl;
	
	int c = 15;
	int d = c & 1;
	cout << "d: " << d << endl;
	return 0;
}

我们可以让值&1,如果为0就是偶数,如果为1就是奇数

获取指定二进制位是1还是0

#include<iostream>
using namespace std;
int main()
{	
	int c = 15;
	//问第4位上是1还是0
	int d = 1;
	d << 3;
	d = d & c;
	d >> 3;
	cout << d<<endl;
	return 0;
}

我们让1先移动到指定位置,然后和我们给的值按位与,在让按位与后的值移动到第一位,打印输出,如果为0原来位上是0,结果为1原来位上为1

交换两个整数变量的值

#include<iostream>
using namespace std;
int main()
{	
	int a = 10;
	int b = 5;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	cout << a << " " << b << endl;
	return 0;
}

三次按位异或就行了

例题

1.如何找出数组中唯一成对的那个数(不开辅助空间)

#include<iostream>
using namespace std;
int main()
{	
	int n;
	cin >> n;
	int* a = (int*)malloc(sizeof(int)*(n+1));
	for (int i = 0; i < n; i++)
	{
		a[i] = i+1;
	}
	int index = rand() % (n - 0);
	a[n] = a[index];
	for (int i = 0; i <= n; i++)
	{
		cout << a[i]<<" ";
	}
	cout << endl;
	int x1 = 0;
	for (int i = 1; i <= n; i++)
	{
		x1 = x1 ^ i;
	}
	for (int i = 0; i <= n; i++)
	{
		x1 = x1 ^ a[i];
	}
	cout << x1;
	return 0;
}

思路就是我让连续的数去连续按位异或,1^2^3^4...^n,我先开一个x1=0,0异或其他任何数都是其他数据,异或完之后,x1再和有重复数据的数组去连续异或,如果一个数出现偶数次就会被消掉,奇数次就会被保留,因为重复数组中那个重复的数K出现了2次,但是我们先异或了一下连续数组,这个K就相当于异或了3次,是奇数次就会保留了下来。

写一个开辅助空间的

#include<iostream>
using namespace std;
int main()
{
	int n;
	cin >> n;
	int* a = (int*)malloc(sizeof(int) * (n + 1));
	for (int i = 0; i < n; i++)
	{
		a[i] = i + 1;
	}
	int index = rand() % (n - 0);
	a[n] = a[index];
	for (int i = 0; i <= n; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
	int x1 = 0;
	for (int i = 1; i <= n; i++)
	{
		x1 = x1 ^ i;
	}
	for (int i = 0; i <= n; i++)
	{
		x1 = x1 ^ a[i];
	}
	cout << x1 << endl;
	cout << "----------------------------------"<<endl;
	int* b = (int*)malloc(sizeof(int) * (n + 1));
	for (int i = 0; i <= n; i++)
	{
		b[i] = 0;
	}
	for (int i = 0; i <= n; i++)
	{
		b[a[i]]++;
	}
	for (int i = 0; i <= n; i++)
	{
		if (b[i] == 2)
		{
			cout << i<<endl;
		}
	}

	return 0;
}

开辅助空间的解法就是,先开一个数组,里面索引范围是我们连续的数据,遍历一遍有重复元素的数组,在对应索引的位置++,如果有一个位置是2,那我们就输出索引

2.找出落单的数

一个数组中除了一个数之外都出现了2次,找出那个数

思路还是一样的,我们一次异或,出现偶数次的都消掉了,奇数次的就会被保留下来,所以出现2次的都会被消掉,1次的就会保留

#include<iostream>
using namespace std;
int main()
{
	int n;
	cin >> n;
	int* a = (int*)malloc(sizeof(int) * (n));
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
	}
	int x1 = 0;
	for (int i = 0; i < n; i++)
	{
		x1 = x1 ^ a[i];
	}
	cout << x1;

	return 0;
}

3.找到某个数二进制中1的个数

思路1:我们给一个数K,每一回都减去一个1,然后和K按位与,这样可以消去一个1

我们只要数进行了多少次上述操作就行了

#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int main()
{
	int N;
	cin >> N;
	int count = 0;
	while (N != 0)
	{
		N = ((N - 1) & N);
		count++;
	}
	cout << count << endl;
	return 0;
}

一开始我做的时候,实现转化成它的二进制(一个字符串),然后遍历这个字符串,找到1的个数,其实也是可以的,只是上述的方法更加简单

4.判断一个数是不是2的整数次

思路:如果二进制中只有一个1,那就是2的整数次

#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int main()
{
	int N;
	cin >> N;
	if (((N - 1) & N) == 0)
		cout << N;
	return 0;
}

5.将数进行奇数位偶数位互换

给出一个数,将其奇数位和偶数位互换,得出一个新的数

思路如下

代码

#include<iostream>
#include<string>
#include<string.h>
using namespace std;
int main()
{
	int i = 0;
	cin >> i;
	int ou = i&0xaaaaaaaa;
	int ji = i&0x55555555;
	cout << ((ji << 1) ^ (ou >> 1))<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值