位运算解决常见问题--求二进制中1的个数,判断是否是2的次幂等

1、什么是位运算?

学计算机的人应该都知道,计算机内部都是以二进制来计算的。那么什么是二进制?我们日常用到的都是十进制,而所谓的十进制就是逢10进1。二进制中只包含0和1,逢2进1。常见的进制还有,八进制,十六进制,二十六进制等。
而位运算就是直接在二进制的数字序列上进行按位与,按位或,按位异或,左移,右移这几种操作。
由于计算机内部本身就是以二进制在运算,所以位运算相较算数运算符来说,计算起来就快的多了。左移一位相当于乘2,右移一位相当于除2,所以很多时候我们可以直接使用移位来替换*/,可以使得代码编写上显得可以更高级一些。但是即使不写,我们的代码在编译运行的时候,编译阶段编译器也会做优化,会用左移来替代乘法,右移替代除法,从而提高代码的效率。
而很多运算问题也都是可以使用位运算来解决,比如,使用位运算来判断一个数中1的个数,一组数字中只出现一次的数字、出现两次的数字,求平均数,交换数,判断一个数是不是2的幂,把一个数改成另外一个数时要改动几个bit位等等。

2、求二进制中1的个数

最开始的思路就是,循环判断每一个bit位是否为1,为1就进行计数。

	int hammingWeight(uint32_t n)
	 {
		if (n == 0)
			return 0;

		int count = 0;
		while (n)
		{
			if ((n & 1) != 0)
				count++;
			n = n >> 1;
		}
		return count;
	}

一开始,也确实觉得这样没什么问题。但是刷剑指offer的时候,书上说的如果当 n = 0x80000000时,也就是形参不是无符号int时。我把 n 的类型改成int之后,程序确实就崩了。所以说,这样的解法还是有缺陷,那么就得研究新的解法了。
根据位运算的特性可知,每次n & (n-1) 都会把位于最右边的 bit 位为1的那一位变为0。从而求得1的个数
在这里插入图片描述
所以代码可以改进如下:

int numberOf(int n)
{
	int count = 0;
	while (n)
	{
		count++;
		n = n & (n - 1);
	}
	return count;
}

3、判断一个数是不是2的次幂

2的次幂有什么特性?在计科专业学习了三年了,不管是硬件方面,还是编程这一方面,2的多少次方是多少,这是一个永远都绕不开的话题。所以一个数是不是2的次幂,那就要判断这个数的 bit 位中是否只有1个1。拿int来说,2^0 = 1 一直到 2的31次方,求2的n次方(0 <= n <= 31),都可以用这32个 bit 位的其中一个表示出来。这么推下来,特性就显而易见了。只需判断bit位中是不是只有1位为1,如果不满足,那肯定不是2的幂。
这是我在力扣上刷题时第一次写的代码:

class Solution {
public:
    bool isPowerOfTwo(int n) 
    {
        if (n<=0)
			return false;
        int bit = 0;
		int num = 0;
		int a = n;
		while (bit < 32)
		{
			num = a & 1;
			if (num != 0)
			{
				int res = pow(2, bit);
				if (n == res)
					return true;
				else if (n < res)
					return false;
			}
			a >>= 1;
			++bit;
		}
		return false;
    }
};

这个代码虽然可以计算出来,但是现在看起来确实很繁琐。所以结合求二进制中1的个数的代码,可以有更好的解法。

class Solution {
public:
	bool isPowerOfTwo(int n)
	{
		if (n <= 0)
			return false;
		if (n & (n - 1))
		{
			return false;
		}
		return true;
	}
};

如果n是2的次幂,那它肯定只有1个 bit 位为1 。所以直接一个 if 语句判断一下就 ojbk 了。

4、给定两个整数m和n,改动m二进制中的多少位,可以得到n

int changebits(int m,int n)
{
	int xor_res = m^n;
	int count = 0;
	while (xor_res != 0)
	{
		count++;
		xor_res = xor_res &(xor_res - 1);
	}
	return count;
}

要改动多少位,那肯定是为1的这些位,m和n中只有一个是。所以异或一下得到异或后的结果,判断这个结果中有多少个1就好。
异或的特性就是“相异为1”,所以用异或来解决的题还有很多。
比如:求一组数据中只出现了一次的数,别的数据都是成对出现;求一组数据中2个只出现了一次的数字,别的都是成对出现。这两道题都可以用异或来解决。

5、位运算求平均数、比较大小、交换值

1)求平均数

int avg(int a, int b)
{
	return (a&b) + ((a^b) >> 1);
}

2)比较大小

bool cmp(int a,int b)
{
	return (a - b) >> 31 ? -1 : (a - b ? 1 : 0);
}

3)交换值

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

位运算确实不好想,所以牵扯到这方面的时候,还是要多写写二进制,看能不能找出规律。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值