剑指 Offer II 003. 前 n 个数字二进制中 1 的个数
题目:
思路:
【1】基于暴力的方式
【2】动态规划 +位运算
对于所有的数字,都可以分为奇数和偶数两种:
奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。
因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。
所以采用动态规划可得一下方程:
dp[i] = dp[i-1],当i为奇数
dp[i] = dp[i/2],当i为偶数
还可进一步合并为: dp[i] = dp[i/2] + i % 2;
如dp[7] = dp[7-1] = dp[6] = dp[6/2] = dp[3];
所以合并公式是正确的,此外
i / 2 可以通过 i >> 1 得到;
i % 2 可以通过 i & 1 得到;
代码展示:
暴力破解的方式:
//时间12 ms击败6.8% //内存46.5 MB击败5% class Solution { public int[] countBits(int n) { int[] res = new int[n+1]; String temp; for (int i = 1; i <= n; i++){ temp = Integer.toBinaryString(i); for(int j = 0; j < temp.length(); j++){ if(temp.charAt(j) == '1'){ res[i]++; } } } return res; } }
动态规划+位运算优化的方式:
//1 ms击败99.95% //内存45.7 MB击败25.22% //时间复杂度:O(n) //空间复杂度:O(1),如果算上返回的结果数组的话其实应该是O(n) class Solution { public int[] countBits(int n) { int ans[] = new int[n+1]; for(int i = 0;i <= n;i++) { if((i&1)==0){ //偶数 ans[i] = ans[i>>1]; }else{ //奇数 ans[i] = ans[i-1]+1; } } return ans; } }
下面这种是合并方程式的写法
//时间2 ms击败39.49% //内存45.4 MB击败75.67% //时间复杂度:O(n) //空间复杂度:O(1),如果算上返回的结果数组的话其实应该是O(n) class Solution { public int[] countBits(int n) { int[] res = new int[n+1]; for (int i = 0; i <= n; i++){ res[i] = res[i >> 1] + (i & 1); } return res; } }