位运算

位运算就是将一个数字用二进制的方式表示出来,对每一位上的0、1进行的运算。

位运算有五种:与、或,异或、左移、右移。

左移运算符:m<<n,表示把m左移n位。

例:00010101<<2=01010100

右移运算符:m>>n,表示把m右移n位。

例:00010101>>2=00000101

题目一

    当我们看到题目,很快就形成一个思路:我们每次先判断一个数的二进制的最右边是不是1,判断完之后整体右移一位,继续判断。用最右边的数和1做&运算,结果如果是1,就表示带整数最右边的一位是,否则就是0;

代码:

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

右移虽然和除以2在数学上是等价的,但是除法的效率要比运算符低得多。

其实上边的代码是不完全对的,如果给的参数是0x80000000,也就是32位的10000000 00000000 00000000 00000000,对它右移就是01000000 00000000 00000000 00000000,但是要保证这个数是个负数,在移位后最高位就会默认为1,就变成11000000 00000000 00000000 00000000,即0xC0000000;如果这样走下去,最终这个数字就变成0xFFFFFFFF,程序就会死循环。

 

为了避免这样的死循环,我们可以换一种思路,让原来的数字不右移,首先让n和1做&运算,让1左移变成00000000 00000000 00000000 00000010,在和n做&运算,这样的反复左移就可以判断出哪一位上有1。

修改代码:

int NumOfOne(int n)
{
	int count = 0;
	unsigned int flag = 1;

	while (flag)
	{
		if (n&flag)
			count++;
		flag = flag << 1;
	}
	return count;
}

这样的代码虽然对,然是如果32位都是1,那么它就要循环32次。

 

究极思路:

我们把一个整数减去1,再和原来的整数做&运算,会把原来的数的最右边的1变成0,那么一个整数中有多少个1就进行多少次这样的操作。

例:1100(12)减1变成1011,再和1100做&运算,得到1000,就是将1100中第二个1变成0。

代码:

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

 

相关题目:

1.用一条语句判断个整数是不是二的倍数

思路:

一个整数如果是二的倍数。那么它的二进制中有且只有一个1,通过“究极思路”,它唯一的1也就变成0;

代码:

void  PowerOfTwo(int n)
{
	if (n==0)//指数函数都大于0
		printf("NO\n");
	else if (((n - 1)&n) == 0)
		printf("YES\n");
	else
		printf("NO\n");
}

2.输入两个整数m,n,计算需要改变m的二进制中多少位,才能变成n。

思路:

例:m:13(1101)                   n:   8(1000)     

       第一步:m^n=0101

       第二部:求异或的数中1的个数

代码:

int MtoN(int m,int n)
{
	int count = 0;
	int a = 0;
	a = m^n;
	while (a)
	{
		++count;
		a = (a - 1)&a;
	}
	return count;
}

  3.写一个函数,求两个整数的和,要求在函数体内不得使用加,减,乘,除四则运算符号。

思路:不用四则运算,还要完成加法,毫无疑问,我们要用到位运算。我们可以分成三步:1.各位相加不进位;2.做进位;3.前两步相加

我们举个例子:15+7

15的二进制1111    7的二进制111

第一步:相加不进位1111+0111=1000,很明显这就是异或运算。

第二部:做进位1111+0111的进位就是1110,第一位的1和0没有进位,剩下三个位的1和1都有进位,这相当于1111&0111左移1位。

因为进位不是0所以继续重复一二步,直到不产生进位为止。

              第一步:1000^1110=0110

              第二步:1000&1110左移一位=10000  (进位不为0)

                             第一步:0110^10000=10110

                             第二部:0110&10000为0;

第三步:10110+0=10110为22

代码实现:

int ADD(int n1, int n2)
{
	int sum, count;
	do
	{
		sum = n1^n2;
		count = (n1&n2) << 1;
		n1 = sum;
		n2 = count;
	}
	while (n2!=0);
	return n1;
}
int main()
{
	int ret = ADD(15,7);
	system("pause");
	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值