问题描述:
给定一个32(2^n)比特的整数,用一个算法计算出其中包含的1的个数。
问题分析:
对于该问题,最容易想到的算法就是穷举算法,对该整数的每个比特进行扫描计数,对于一个包含n比特的整数,该算法的时间复杂度为O(n),空间复杂度为O(1)。
算法如下(C语言):
int count(unsigned int i)
{
int count = 0;
for(int j=0;j<32;j++)
{
if((i>>j) & 1)
count++;
}
return count;
}
算法解析:
该算法在最好和最坏情况下的复杂度一样,都为O(n), n为比特数。
第二种方法:
对于第二种方法,其最坏复杂度和第一种一样,但最好的情况下为O(1),平均复杂度仅为第一种的一半。
算法(C语言):
int count(unsigned int i)
{
int count = 0;
while(i)
{
count++;
i = i & (i-1);
}
}
该算法仅对比特为1的进行计数,每次循环计算一个比特为1的位,该算法的时间复杂度还是O(n),但是效率比之第一种算法平均要快上一倍。
第三种算法:
这种算法的时间复杂度为O(logN),还是先给出算法的描述,然后再进行分析。
算法(C语言):
int count(unsigned int i)
{
i = (i & 0x55555555UL) +(( i>>1) & 0x55555555UL);
i = (i & 0x33333333UL) + ((i>>2) & 0x33333333UL);
i = (i & 0x0F0F0F0FUL) + ((i>>4) & 0x0F0F0F0FUL);
i = (i & 0x00FF00FFUL) + ((i>>8) & 0x00FF00FFUL);
i = (i & 0x0000FFFFUL) + (i >>16) & 0x0000FFFFUL);
return i;
}
算法分析:
该算法首先将32比特的整数分成16组,每两个相邻的比特分为一组,分组计算每组的比特为1的数量,
对于一个整数如:xyxyxyxyxyxyxyxyxyxyxyxyxyxyxyxy,b分为两部分后,第一部分为i & 0x55555555UL,
第二部分为:(( i>>1) & 0x55555555UL),由于0X55555555UL的二进制为01010101010101010101010101010101b, 这样,用i与之进行与操作之后,相当于只是取了奇数部分的比特,结果为0y0y0y0y0y0y0y0y0y0y0y0y0y0y0y0yb,
而第二部分则先进行一个比特的移位之后,再进行一个与操作,结果为0x0x0x0x0x0x0x0x0x0x0x0x0x0x0x0xb,
(x和y为0或者1),然后把这两部分的结果相加,可以看出,0x + oy结果可能为00,01,10, 而这两比特中所保存的结果就是在原来的整数中xy部分的比特为1的个数。(如果1为1,y为0,则只有一个1比特,此时结果为01,同样,若x,y均为1,则结果为10,也是表示只有2个比特为1,其它为0或者1的情况同样成立),这样在结果i中保存16组(每组2比特)的1的个数,然后再将这个16组分成8组,每组四比特(即前面的2个2比特),将4比特中的前2比特进行移位与后两比特对齐,再进行相加,此时在结果4比特中的保存的就是这原来4比特中1的个数(因为前面的结果中每组2比特中最大为10,这样相加后,最大值为0100,表示的还是这4个比特中为1的个数,而且不会向前部分产生进位),然后再放大到8比特组,最后到16比特组,得到的结果就是该整数中比特为1的个数。