您的位置:首页 > 其它

如何以最优时间复杂度得出——判断一个字节内的整数的二进制形式中的1个数

2010-09-08 09:42 387 查看
声明:

1、本文全文为半香仙笛(Woody.c)原创

2、如果需要转载本文,请注明出处:http://blog.csdn.net/mcg890414/archive/2010/09/08/5870044.aspx

3、如果需要和笔者交流,请邮件至mcg890414@163.com

Thinking in C。。

最近每日比较大量的工作,是为了找工作而做面试题。

做了许多智力型的面试题,觉得自己思维,打个85+分不为过吧,毕竟还是智商超过140的人。

昨日看到一道关于“如何以最优时间复杂度得出——判断一个字节内的整数的二进制形式中的1个数”。

输入:

正整型,1个。要求在一个字节内(0~255)。

输出:

整形,1个。是输入的数的二进制形式中1的个数(应该在0~8之间)。

示例——

输入:

33

输出:

2

题目出得很好。

我有思考。

最初想,循环右移。当结果不为0得失后继续右移。并计数。

写出代码如下:

int count(int n)
{
int ret = 0;

while(n)
{
if(n & 1)
{
ret++;
}
n = n>>1;
}
return ret;
}


这个算法,时间复杂度约为O(m),也就是说,需要循环的次数,为最高一个1所在位数的位置所决定,越靠左,循环次数越高,最多可达8次。

我觉得,一般的程序员都能想到这个算法,所以,面试题肯定不会给这么简单的答案。

继续思考。。。

我想,有没有一种算法,能够不由某一个1决定,而由所有的1决定呢?也就是说,数n次,n是这个二进制中1的个数?

所以,有了下面的算法:

int count(int n)
{
int ret = 0;

while(n)
{
n = n&(n-1);
ret++;
}
return ret;
}


读起来是不是有些费解?呵呵,没关系,我来解释一下,如果你已经理解了,请跳过这段解释。

这个算法把二进制中的最后一个1变为0,若其不是最后一位,将其之后的所有0变为1(n-1的功效,强大吧),然后拿这个结果来位与。这样的话,每次位与就消除最后一个1。

没明白?好,我们来看个例子:

加入我们有输入:

11001001

第一次减1,得到

11001000

位与结果:

11001001

& 11001000

--------------------

11001000

第二次,减1,得到

11000111

位与结果:

11001000

& 11000111

-------------------------

11000000

第三次,减1。,得到

10111111

位与结果

11000000

& 10111111

-------------------------

10000000

第四次,减1,得到

01111111

位与结果

10000000

& 01111111

--------------------------

00000000

结束。

计数4次,得到返回结果为4。

你懂了么?

如果你还没懂,请给我邮件:mcg890414@163.com

好了,第二个算法,较第一个时间复杂度更优。

不过,我们还可以以空间换时间,不过,如果给的输入不是8位,每多1位,需要的工作就要大两倍。

我们来看时间复杂度为O(1)的算法:

int ntbl[256] = {	0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4,
2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4,
...
};// ntbl
是n的二进制表示中1的个数
int count(int n)
{
// check input
return ntbl
;
}


这个算法有点暴力,可以提前用之前的算法运算出来一个表,然后放入之前写好的一个数组中,直接输出这个输入作为下标的数组元素即可。

当然,这个算法是用空间来换时间的。作为思维训练是可以的,但是真正的开发中,没有人会推荐你使用这个算法。

好了,先聊到这,Then....

你有更好的算法么?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: