版权声明:本文为博主原创文章,转载需标注来源。 https://blog.csdn.net/Code_Mart/article/details/78630638
今天舍友刷题,遇到一个题目,对于某一给定数字num,求[1,num]区间里每个整数的二进制表示所含1的个数。
e.g. num = 5 则 answer[] = {1,1,2,1,2}
对这道题的最基础的解法是,对于区间里的每个整数进行移位,直到遍历该整数的二进制表示的所有位。
code:
for (int i = 1;i<=num;i++)
{
int ans = 0, m = i;
for(int j = sizeof int ;j>0;j--)
{
ans+=(m&1);
m=m>>1;
}
answer[i] = ans;
}
这样的算法时间复杂度为O(n*sizeof int),但是有比这更好的算法,可以达到O(n)的复杂度。
回忆一下树状数组,有个操作要求给定整数的lowbit,即求该整数的二进制表示中最右侧的1的右侧所含0的个数,我们使用的办法比较简单:i&(-1)。假如最右侧1的右侧是所含0的个数为k,我们这样就可以直接求得2^k.
下面所讲的算法与其有一定的相似之处。
code:
answer[0] = 0;
for (int i = 1;i<=num;i++)
answer[i] = answer[i&i-1] +1;
代码非常简洁,也非常容易理解。初始化边界,即answer[0] = 1。
我们知道,对于任意一个整数i而言,i的二进制表示的最右侧的1,以及从该位置起的右侧所有位置,都与i-1的二进制表示中的对应位置不同。所以,i的二进制表示中所含1的个数应为其最右侧1的左侧所有1的个数加1.