位运算的简单应用

概述

在学习程序语言时,并没有感受到位运算的用处,只知道左移右移可以代替乘法除法且效率较高。学习完CASPP后,发现其实位运算还是比较重要的,它更贴近于底层,最近写的一些题目中,发现位运算有许多意想不到的作用,可以巧解一些题,在这里做一下总结和梳理。

位运算符号

& : 与运算——按位与,两个位为1则与运算之后为1,其余情况为0
| :或运算——按位或,两个位为0则或运算之后为0,其余情况为1
^ : 异或运算——两个位相同异或为0,否则为1
~ :取反运算——对二进制每个位取反,即0变为1,1变为0
<< : 左移运算——将二进制位向右移,右边补0,n<<1, 即n*2(无符号未溢出)

应用

1、判断一个数的奇偶性

bool is_odd(int x)//返回为true为奇数,false为偶数
{
	if(x&1) return true;
	else return false;
}

x&1其实就是判断x的二进制最低位是1还是0,若为1,则x&1为1,该数为奇数,反之,x&1为0,则该数为偶数。

2、判断二进制数某位是1或0

bool is_zero(int x,int n)//n为该二进制数,x为第x位,假设该数共32位
{
	if(n & (1<<(32-x))) return true;
	else return false;
}

其实这个判断和判断奇偶性是类似的,也是判断二进制数某一位是否为0或1,只需要用左移操作移动到判断的哪一位即可。

3、判断某数是否为2的幂次

bool is_twopower(int n)//true为是2的幂
{
	if(n&(n-1)==0) return true;
	else return false;
}

一个数若为2的幂,则二进制的数中只有一个1,n&(n-1)可以将唯一的1个1消去,故当为0时,则是2的幂。

4、交换数值

void swap(int &a,int &b)
{
	a=a^b;
	b=a^b;
	a=a^b;
}

第一行:a=a^b;
第二行:b=(a^b) ^b, 因为 b ^ b=0,所以b = a^0 = a;
第三行:a=a^b= a^a ^b=0 ^ b=b;
采用一些异或的计算做到了两个数的交换

5、找出不重复的数

首先是最简单的一种情况,只有一个数出现一次,其余数都出现两次。
因为相同的数异或之后为0,故将所有的数都异或的结果就是那个出现一次的数。

int find_data(vector<int>& num)
{
	int res=0;
	for(int i=0;i<num.size();i++)
	{
		res=res^num[i];
	}
	return res;
}

第二种情况,只有一个数出现一次,其余的数都出现三次。
这种情况有两种位操作的解法:
第一种,是将每一个整数用32位表示,记录所有整数每一位是1的个数sum,sum对3取余数,如果余数不为0,则出现一次的那个数对应位上为1,通过左移操作将每一位数移动到相应的位置上。

int find_data(vector<int>& num)
{
	int res=0;
	for(int i=0;i<32;i++)
	{
		int sum=0;
		for(int j=0;j<num.size();j++)
		{
			sum+=(num[i]>>i)&1;//计算位1的个数
		}
		res=res|(sum%3)<<i;//得到出现一次的数相应二进制位上的数
	}
	return res;
}

这种解法可以解决一串数只有一个数出现一次,其余数出现k次的情况

第二种解法是使用状态机法,用k-1个数表示状态机的k个状态。
在这里有三个状态,其实也就是模3之后余数的个数,0,1,2,用二进制表示分别为00 01 10,由于用二进制的两个位来表示,因此可以用两个变量来表示两个位,普遍来讲,可以使用一个数组来表示各位的数。

int find_data(vector<int>& num)//这里是2个状态的代码,使用变量
{
	int bits_one=0,bits_two=0;
	for(int i=0;i<num.size();i++)
	{
		bits_one=(bits_one^num[i])&~bits_two;
		bits_two=(bits_two^num[i])&~bits_one;
	}
	return bits_one;
}

而这里的位操作是非常的复杂和巧妙,我是想不出来……
在这里有大神的具体讲解:LeetCode题解

6、二进制枚举子集

枚举一个集合的幂集,可以采用二进制的算法,将二进制的一位对应集合中一个元素的取或者不取,1表示选取,0表示未选取。

vector<vector<int>> power_set(vector<int> nums)
{
	vector<vector<int>> res;
	int len=nums.size();
	for(int i=0;i<(1<<len);i++)//有1<<len个幂集
	{
		vector<int> temp;
		for(int j=0;j<len;j++)
		{
			if(((1<<j)&i)!=0) temp.push_back(nums[j]);
			//第j位的元素未被选取
		}
		res.push_back(temp);
	}
	return res;
}

结语

位操作是一个强大的工具,还有许多的应用等待着我们去发现…………

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值