一.算法题干
给定一个非负整数num
。对于0 ≤ i ≤ num
范围中的每个数字i
,计算其二进制数中的1的数目并将它们作为数组返回。
二.样例
样例1:
输入:2
输出:[0,1,1]
样例2:
输入:5
输出:[0,1,1,2,1,2]
三.解题思路
我一开始想到的是最正统的解法:遍历[0,num]
的所有整数,然后对每个整数都不断进行“模2+除以2”的操作,统计这当中模2后得到1的数量(其实也就是转化为二进制后1的个数),最后输出各个小结果组成的vector。
四.实现代码
vector<int> countBits(int n) {
vector<int> resv;
for(int i=0;i<=n;++i)
{
int t=i,res=0;
while(t)
{
if(t%2) ++res;
t/=2;
}
resv.push_back(res);
}
}
五.对比分析
通过观察别人写的代码我发现我针对该问题的实现方式还不够巧妙。别人的代码实现如下图所示。
vector<int> countBits(int num) {
vector<int> result(num+1);
result[0]=0;
for(int i=0;i<=num;++i)
{
if(i%2==1) result[i]=result[i-1]+1;
else result[i]=result[i/2];
}
return result;
}
该解题方法的设计主要是考虑到了奇偶数交替变化中隐藏的规律,运用了DP(动态规划)的思想。动态规划的基本原则就是根据之前已经得到的结果,结合公式推演得到当前的结果,其特点是当前结果依赖于子问题的结果。动态规划的核心有两点:找到递推公式、确定起始状态。在本题中,递推公式的确定依赖于针对整数奇偶性的分类。具体的分类规则如下:针对奇数,在二进制表示中,奇数一定比其前面那个偶数多一个1,因为多的就是最低位的1;针对偶数,在二进制表示中,偶数中1的个数一定和除以2后得到的那个数一样多(因为最低位是0,除以2 就是右移一位,也就是把那个0 抹掉而已,所以1 的个数是不变的)。初始状态,也就是整个递推的起点,即0只有0个1。由此可以得到整个算法的实现方法。