338比特位计数
链接:https://leetcode-cn.com/problems/counting-bits/solution/yi-bu-bu-fen-xi-tui-dao-chu-dong-tai-gui-3yog/
我的解法:
class Solution {
public int[] countBits(int n) {
int[] bits = new int[n + 1];
for (int i = 0; i <= n; i++) {
bits[i]=Integer.bitCount(i);
}
return bits;
}
}
时间复杂度: O(N),因为遍历了一次。
空间复杂度:O(1),返回结果占用的空间不计入空间复杂度中。
其中bitCount实现的功能是计算一个(byte,short,char,int统一按照int方法计算)int,long类型的数值在二进制下“1”的数量。
递归解法:
如果 第 i 是偶数:
那么它的二进制 1 的位数与 i / 2的二进制 1 的位数相等;因为偶数的二进制末尾是 0,右移一位等于 i / 2,而二进制中 1 的个数没有变化。
比如对于 4 和 2:
数字 二进制 二进制中 1 的个数
4 100 1
2 10 1
如果 第i 是奇数:
那么它的二进制 1 的位数 = i - 1 的二进制位数 + 1;因为奇数的二进制末尾是 1,如果把末尾的 1 去掉就等于 i - 1。又 i - 1 是偶数,所以奇数 i 的二进制 1 的个数等于 i/2中二进制 1 的位数 +1.
比如对于 5 和 4:
数字 二进制 二进制中 1 的个数
5 101 2
4 100 1
通过上面的分析我们可以看出可以根据递归解决:
class Solution {
public int[] countBits(int n) {
int[] bits = new int[n + 1];
for (int i = 0; i <= n; i++) {
bits[i]=getnum(i);
}
return bits;
}
public int getnum(int num){
if(num == 0){
return 0;
}else if(num&1== 0){
// >>相当于除2这样比直接/2快
return getnum(num >> 1);
}else{
return getnum(num-1)+1;
}
}
}
- 时间复杂度: O(N ^ 2),因为遍历了一次,每次求解最多需要递归N/2次。
- 空间复杂度:O(N),递归需要调用系统栈,栈的大小最多为 N / 2。
方法三:记忆化搜索
在我们上面递归解法中,其实有很多重复的计算,比如当 i = 8 的时候,需要求 i = 4, 2, 1, 0 的情况,而这些取值已经计算过了,此时可以使用记忆化搜索。
所谓记忆化搜索,就是在每次递归函数结束的时候,把计算结果保存起来。这样的话,如果下次递归的时候遇到了同样的输入,则直接从保存的结果中直接查询并返回,不用再次递归。
class Solution {
int res[];
public int[] countBits(int n) {
int[] bits = new int[n + 1];
for (int i = 0; i <= n; i++) {
bits[i]=getnum(i);
}
return bits;
}
//试一下递归做法,就是操作res
public int getnum(int num){
if(num == 0){
return 0;
}
//这里就是进行的记忆话搜索
if(res[num]!=0){
return res[num];
}
int ans=0;
if(num&1 == 0){
ans=getnum(num >> 1);
}else{
ans=getnum(num-1)+1;
}
//不能只读啊 写肯定也要
res[n]=ans;
return ans;
}
}
时间复杂度: O(N),因为遍历了一次,每次求解都可以从之前的记忆化结果中找到
空间复杂度:O(N),用到了辅助的空间保存结果,空间的结果是 O(N)
方法四:动态规划
其实很多时候,动态规划的方法都是从记忆化搜索中优化出来的。本题也可以如此。
方法三在记忆化搜索过程中,我们看到其实每次调用递归函数的时候,递归函数只会运行一次,就被 res[num] 捕获并返回了。那么其实可以去除递归函数,直接从 res 数组中查结果。
class Solution {
public int[] countBits(int num) {
int[] result=new int[num+1];
result[0]=0;
for(int i=1;i<num+1;i++){
if(i&1==0) result[i]=result[i>>1];
else result[i]=result[i-1]+1;
}
return result;
}
}
- 时间复杂度: O(N),因为遍历了一次。
- 空间复杂度:O(1),返回结果占用的空间不计入空间复杂度中。