Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目:给定一个整数n,现要求对在整数范围内0<=i<=n的每个元素都计算二进制表示中1的个数,并且返回数组作为其答案。
解题思路
要解答本题,首先要明白十进制数如何转换为二进制,例如对于十进制数10,我们可以用除2取余,逆序排列的方法。具体来说即:
最终可得到10的二进制表达为1010。
根据上述思路,我们可以直接用整除的方法来计算1的个数,对于一个数,其对2取余只可能为0或者1,首先看对2取余是否为1,是则直接更新1的个数,之后更新当前值为用2整除后的数。否则直接更新,一直循环直到即可。可得代码如下:
public int[] countBits(int n) {
int[] result = new int[n + 1];
for(int i=0;i <= n;i++){
int count = 0, cur=i;
while(cur >> 1 != 0||cur % 2 != 0){
if(cur % 2 == 1) count++;
cur = cur >> 1 ;
}
result[i] = count;
}
return result;
}
因为算术运算需要转化为位运算,因此此处直接用右移代替除法,时间复杂度为。
进阶解法
题目要求使用时间复杂度为的算法来解决该问题,那么此时就需要扫描一遍数就得到所有答案。首先想到的就是动态规划,动态规划可以根据前面的答案来获得后面的答案。
本题是否可以使用动态规划呢?我们可以先打印n=15时的答案:
[0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]
仔细观察,的答案永远是1,而能被2整除的,其答案和整除后的数答案一样,不能被2整除的,其答案为被2整除后的那个数答案加1的答案,最终可得代码:
public int[] countBits2(int n) {
if(n==0) return new int[]{0};
int[] dp = new int[n + 1];
dp[0] = 0;
dp[1] = 1;
for(int i=2;i <= n;i++){
if(i%2==0){
dp[i] = dp[i >> 1];
}else {
dp[i] = dp[1] + dp[i >> 1];
}
}
return dp;
}
时间复杂度为。