191117题

首先我们知道,计算机以补码的形式存储的,正数的补码是原码不变,负数补码是通过原码计算得到,计算过程为:符号位不变,其余位按照原码取反加1。

对于8位有符号数(-128~ 0~127):
+127,原码0111 1111
对于-128我们来看,-128是个特殊的数,不用考虑符号位!若字长为1byte,因有一位是符号位,所以原码能表示数值的范围为(-127~ -0 ,+0~ 127)共256个。注:在补码中用(-128)代替了(-0),所以补码的表示范围为:(-128~ 0~127)共256个.
注:-128原码和补码一样的, 为1000 0000
下面,我们来看一下-129的值,-128的原码为 1000 0000 ,减1 等于0111 1111 (原码计算就没有那些规矩了,直接计算就行了),然后存储,计算机一看正数,就直接存储了+127.

左移运算符m<<n表示把m左移n位。往左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0.
如:

00001010<<2=00101000
10001010<<3=01010000

右移运算符m>>n表示把m右移n位。往右移的时候,稍微有些复杂。
对于无符号数,丢弃最右边的n位,并用0填补最左边的n位。

对于有符号数:先转化为补码,丢弃最右边的n位,并用该数字的符号位填补最左边的n位。

下面是对两个8位有符号数(均为补码形式下)进行右移的例子:

00001010>>2=00000010
10001010>>3=11110001

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接下来看题
在这里插入图片描述
思路1(有bug):
对输入的number和1按位与,这样就可以判断number转化为二进制后的最右位是否为1,如果为1,count++,然后把number右移一位,继续循环,直到number为0为止。
但这里存在致命的问题在于,如果输入的number是负数,比如一个负数的补码为0x80000000(32位),右移一位后最左边的一位会被1补上,即变成0xC0000000,如果一直这么右移下去,那么最终这个数字就会变成0xFFFFFFFF而陷入死循环。

改进的思路2(也是大部分人能想到的常规思路):
为了避免死循环,不对输入的number进行右移。首先把number和1(记为flag)进行按位与,判断number最右位(最低位)是不是1,然后把flag左移一位得到2,再和number按位与,判断number次低位是不是1。这样反复即可,最终flag为0时结束循环。总共的循环次数就是整数二进制的位数,32位的整数就要循环32次。
代码如下:

class Solution {
public:
	int NumberOf1(int number)
	{
		int count = 0;
		int flag = 1;
		while (flag)
		{
			if (number&flag)
				count++;
			flag = flag << 1;
		}
		return count;
	}

};

思路3(感觉如果实际面试,很难想到):
把number和number-1按位与,如果结果不为0,说明number中在最低位上的1被去除了。
如:
number为10110100
number-1为10110011
number&(number-1)为10110000,和number对比后发现number中在最低位上的1被去除了。
这样循环的次数减少了,number的二进制数中有几个1,就循环几次。
代码如下:

class Solution {
public:
	int NumberOf1(int number)
	{
		int count = 0;
		while (number)
		{
			count++;
			number = number&(number - 1);
		}
		return count;
	}

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值