题目来源:力扣
题目描述:
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
================================================
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
=============================================
给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
审题:
最简单的方法是检查每一整数的比特位,计算为1的个数.此处我们考虑其他时间复杂度更小的方法.
首先观察连续整数的比特数组,{0, 1, 10, 11, 100, 101, 110, 111},观察以上序列我们可以发现如下规律,2的比特位中1的个数等于0的比特位中1的个数+1,3的比特位中1的个数等于1的比特位中1的个数+1,4的比特位中1的个数等于0的比特位中1的个数+1,5的比特位中1的个数等于1的比特位中1的个数+1.我们使用
S
[
i
]
S[i]
S[i]表示整数i中比特位为1的个数,则可以推导出如下公式:
S
[
i
]
=
S
[
i
%
2
(
i
n
t
)
l
g
2
i
]
+
1
S[i] = S[i \% 2^{(int)lg_{2}i}]+1
S[i]=S[i%2(int)lg2i]+1,且
S
[
0
]
=
0
,
S
[
1
]
=
1
S[0] = 0, S[1] = 1
S[0]=0,S[1]=1.
基于以上最优子结构推导,我们可以使用动态规划算法自底向上依次计算S[0], S[1], …S[n].
还有另一中更简洁的方法,在看了此方法后,深感巧妙.所有整数可分为奇数和偶数两种,如果一个数是奇数,则其前一个数是偶数,并且该奇数位为1的个数等于前一偶数位为1个数加1,因为相较于前一偶数,除最低位由0变为1外,其余位均相同.如果当前数为偶数,则该偶数中位为1的个数等于该偶数除以2对应偶数中位为1的个数,因为除以2相当于将当前偶数右移一位,由于右移的最低位为0,因此移位后整数为1的个数不变.我们可以推导出如下最优子结构:
i
f
if
if
i
&
1
=
=
1
,
S
[
i
]
=
S
[
i
−
1
]
+
1
i\&1==1, S[i] = S[i-1]+1
i&1==1,S[i]=S[i−1]+1
e
l
s
e
else
else
S
[
i
]
=
S
[
i
>
>
1
]
S[i] = S[i >> 1]
S[i]=S[i>>1]
java算法:
class Solution {
public int[] countBits(int num) {
if(num == 0)
return new int[]{0};
int[] S = new int[num+1];
S[0] = 0;
S[1] = 1;
for(int i = 2; i <= num; i++){
int mod = (int)Math.pow(2, (int)(Math.log(i) / Math.log(2)));
S[i] = S[i % mod] + 1;
}
return S;
}
}
位运算版本
class Solution {
public int[] countBits(int num) {
if(num == 0)
return new int[]{0};
int[] S = new int[num+1];
S[0] = 0;
S[1] = 1;
for(int i = 2; i <= num; i++)
if((i & 1) == 0)
S[i] = S[i >> 1];
else
S[i] = S[i-1] + 1;
return S;
}
}