300. 最长递增子序列 | 动态规划 |暴力递归 | 自顶向下分析方法

力扣打卡:300. 最长递增子序列

解题思路

  • 动态规划的题目还是得先写出了暴力递归后在考虑自顶向下的分析方法

  • 再写暴力递归的函数时,不要想着任何的优化,最主要的还是能写出暴力递归的函数

  • 定义的函数,就是为了能够得到结果,在使用时直接当作能得到类似子问题的结果,然后与当前的状态进行关联

  • 一定要分析出当前状态和子问题状态的关系,也就是系统的状态转移方程

代码

class Solution {
    public int lengthOfLIS(int[] nums) {
        // 状态分析:每一个元素找到左边比自己小的元素,看这个元素有多少个长度
        // 如果左边都没有比自己小的元素了,那么返回自身的一个1
        
        // int max = 0;
        // for(int i=nums.length-1; i>=0; i--){
        //     max = Math.max(max, planA(nums, i));
        // }
        // return max;

        // int[] memo = new int[nums.length];
        // Arrays.fill(memo, -1); // 将memo的元素置为与题目无关的数字,方便进行判断
        // int max = 0;
        // for(int i=nums.length-1; i>=0; i--){
        //     max = Math.max(max, planB(memo, nums, i));
        // }
        // return max;

        int[] memo = new int[nums.length];
        Arrays.fill(memo,-1);
        int max = 0;
        for(int i=nums.length-1; i>=0; i--){
            max = Math.max(max, planC(memo, nums, i));
        }
        return max;
    }

    // 状态分析:需要找到最长的长度
    // 对于当前的元素 a 从右向左遍历,找到当前比自己小的元素 x,然后进入子问题状态,检查比 元素 x 小的元素
    // 定义的函数就是:
    // 能够得到比自己小的元素的元素个数,定义的函数功能就是这样,不要做任何的优化
    // 停止条件:索引小于0,或者是索引等于 0 时,表明左边已经没有索引了

    public int planA(int[] nums, int p){ // 函数的功能是找到最长的子序列
        if(p<0) return 0;
        if(p==0) return 1; // 如果是第一个数,那么直接返回1,应为此时左边没有数了,所以应该返回自身的长度1

        int max = 0;
        for(int i=p; i>=0; i--){ // 检查左边任何一个自己小的元素,然后又重新检查比自己小的元素的元素的个数
            if( nums[p] > nums[i]){ // 找到比自己小的数字
                int cnt = planA(nums, i); // 得到子问题的结果,子问题中找到比子问题元素还小的个数,与当前的max进行比较
                max = Math.max(max, cnt); // 各个子问题的结果和当前的max进行比较
            }
        }

        return max+1; // 当前的长度也要计算进去
    }


    // 写出了暴力递归的方法,那么也就是写出了自顶向下的动态规划
    // 因为需要求的个数也就是数组的每一个元素左边比自己小的个数的最大值,那么 memo 就是数组元素的个数的长度,元素的内容就是最长序列的个数
    public int planB(int[] memo, int[] nums, int p){
        if(p<0) return 0; // 这个终止条件可以不写
        if(p==0) return 1; // 不是说第一个元素最小,而是因为在for循环中,只有检查到了比自己小的数,才进行递归,所以这个数就是为大于第一个准备的
        if(memo[p] != -1) return memo[p];

        int max = 0;
        for(int i=p; i>=0; i--){ // 遍历左边的元素
            if(nums[p] > nums[i]){ // 摘掉了比自己小的元素
                int cnt = planB(memo, nums, i);
                max = Math.max(max, cnt);
            }
        }

        memo[p] = max + 1; // 将记录储存起来,同时要记录自身的长度,所以应该是max+1

        return memo[p];
    }

    // 检查planB的for循环,既然子问题的过程是最优解了,那直接存入memo不就可以了,减少更多的计算
    public int planC(int[] memo, int[] nums, int p){
        if(p<0) return 0;
        if(p==0) return 1;
        if(memo[p] != -1) return memo[p];

        int max = 0;
        for(int i=p; i>=0; i--){ // 从右往左开始遍历元素
            if(nums[p]>nums[i]){ // 找到比自己小的元素
                int cnt = planC(memo, nums, i); // 得到子问题同样的结果,使用planc可以直接得到,定义的函数就是能得到结果
                memo[i] = cnt;// 此时不需要要加上1,因为只是求出了子问题的结果
                max = Math.max(max, cnt);
            }
        }

        memo[p] = max + 1;

        return  memo[p];
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值