《编程之美》读书笔记1——求二进制数中1的个数

前言:又到了一周一次的写博客环节了,嗯···没什么存货了,上一周没有看多少关于数据结构的内容,也没记什么笔记;我挣扎了一下,想停更一周,但是好不容易坚持了9周,一定要坚持到今年考研结束,甚至坚持到以后。我决定借鉴一下上次凑单买的《编程之美》的一个关于位运算的案列,“水”一期博客。

一、问题提出

  • 对于一个字节(8bit)的无符号整型变量,求其二进制表示中"1"的个数
  • 相关概念补充:1字节包含8位,C语言用字节(byte)表示存储系统字符集所需的大小(注意:计算机界通常用默认一字节是8位的)。如下图所示,从左往右给这八位分别编号7~0。在1字节中,编号为7的位被称为高阶位,编号是0的位被称为低阶位;每一位对应的编号对应2相应的指数。

《编程之美》读书笔记1——求二进制数中1的个数_第1张图片

  • 这里,128是2的7次幂,以此类推;该字节能表示的最大数字都设置为1:11111111.则这个二进制数的值为:
  • 128 + 64 + 32+ 16 + 8 + 4+ 2 +1 = 256
  • 而该字节最小的二进制数是00000000,其值是0;因此,一字节可以存储0~255范围内的数字,共256个。

二、分析与解法
1、解法一;O(log2v)

  • 对于二进制操作,每除一个2,原来的数字将会减少一个0,如果除的过程有余,就表示当前位置有一个1;
  • 以10100010为例:
  • 第一次除2,商为1010001,余0;
  • 第二次除2,商101000,于1.
  • 因此,可以根据上述特点,让一个二进制数依次除以2,判断余数是否为1来计数1的个数。
int count(byte v)
{
     
	int num = 0;
	while(v)
	{
     
		if(v%2 ==1)
		{
     
			num++;
		}
		v = v/2;
	}
	return num;
}

2、解法二;O(log2v)

  • 使用向右移的位操作同样可以达到相除的效果,这里的问题是如何判断是否有1处在?
  • 解决方法,用该八位数与00000001相”与",如果结果为1,则表示当前的八位数最后一位是1,否则为0.
int count(byte v)
{
     
	int num = 0;
	while(v)
	{
     
		num + = v & 0x01;
		v >>= 1;    //v每位数字向右移动1
	}
	return num;
}

3、解法三;O(M)M为1的个数

  • 前面两个解法,都需要经过8次相除或者移位来累计1的个数,能不能只考虑1所在的位置,跳过0的位置,来直接累计呢?
  • 假设只有一个1:01000000;
  • 判断操作为:01000000&(01000000 - 00000001) =01000000&00111111 =0;
  • 即通过v直接减1跳过0所在的位
int count(byte v)
{
     
	int num = 0;
	while(v)
	{
     
		v &= (v-1);
		num++;
	}
	return num;
}

4、解法4;O(1)
以空间换时间,直接将八位二进制数所有的可能情况预先算出写入表中,遇到时直接查询即可,适用于频繁使用的算法中;

/* 预定义结果表 */
int countTable[256] =
{
     
	0,1,2...//懒得打字了
	...
	...
};
int count(byte v)
{
     
//查表
return countTable[v];
}

解法1和2很多人都想得到,而3、4可能有少数老哥能想到,这也许是灵机一动的头脑风暴,但是大多数因该是见多识广的经验使然。
结束——

你可能感兴趣的:(读书搬运笔记)