题目及测试
package pid338;
/* 338. 比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1] 0 01 10
示例 2:
输入: 5
输出: [0,1,1,2,1,2] 0 01 10 11 100 101 110 111
进阶:
给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
要求算法的空间复杂度为O(n)。
你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。
*/
public class main {
public static void main(String[] args) {
int[] testTable = {2,5};
for (int i=0;i<testTable.length;i++) {
test(testTable[i]);
}
}
private static void test(int ito) {
Solution solution = new Solution();
int[] rtn;
long begin = System.currentTimeMillis();
System.out.print(ito+" ");
System.out.println();
rtn = solution.countBits(ito);//执行程序
long end = System.currentTimeMillis();
System.out.println("rtn=" );
for (int i = 0; i < rtn.length; i++) {
System.out.print(rtn[i]+" ");
}//打印结果几数组
System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
}
}
解法1(成功,2ms,较快)
可以看0-1与2-3
00 01
10 11
可以看到2-3就是在0-1的前面加了个1,所以num[2]=num[0]+1,num[3]=num[1]+1
000 001 010 011 100 101 110 111
可以看到4-7同样也是比0-3每个都多了一个1
可以认为num[i]=num[i-size]+1 size为刚刚小于等于i的2^n
这种方法实际是动态规划 + 最高有效位(与其他结果的最高有效位不同)
package pid338;
class Solution {
public int[] countBits(int num) {
if(num == 0){
return new int[]{0};
}
if(num == 1){
return new int[]{0,1};
}
if(num == 2){
return new int[]{0,1,1};
}
if(num == 3){
return new int[]{0,1,1,2};
}
// 处理>=4的情况
int[] result = new int[num+1];
result[0] = 0;
result[1] = 1;
result[2] = 1;
result[3] = 2;
int size = 4;
for(int i=4;i<=num;i++){
result[i] = result[i-size]+1;
if(i == size * 2 - 1){
size = size * 2;
}
}
return result;
}
}
解法2(别人的)
遵循上一方法的相同原则,我们还可以通过最低有效位来获得状态转移函数。
观察x 和 x′=x/2 的关系:
x=(1001011101)2=(605)10
x′=(100101110)2=(302)10
可以发现 x′ 与 x 只有一位不同,这是因为x′ 可以看做 x 移除最低有效位的结果。
这样,我们就有了下面的状态转移函数:
P(x)=P(x/2)+(xmod 2)
public class Solution {
public int[] countBits(int num) {
int[] ans = new int[num + 1];
for (int i = 1; i <= num; ++i)
ans[i] = ans[i >> 1] + (i & 1); // x / 2 is x >> 1 and x % 2 is x & 1
return ans;
}
}
解法3(别人的)
动态规划 + 最后设置位
与上述方法思路相同,我们可以利用最后设置位。
最后设置位是从右到左第一个为1的位。使用 x &= x - 1 将该位设置为0,就可以得到以下状态转移函数:
P(x)=P(x&(x−1))+1;
public class Solution {
public int[] countBits(int num) {
int[] ans = new int[num + 1];
for (int i = 1; i <= num; ++i)
ans[i] = ans[i & (i - 1)] + 1;
return ans;
}
}