由简单题一步步推导到动态规划

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),返回结果占用的空间不计入空间复杂度中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值