LeetCode:300 最长上升子序列 (最优方法是:动态规划 + 二分法)

第一种简单粗暴地解法

递归求解 状态转移方程,属于Up-Down方法,因为子问题的重复求解,导致时间复杂度O(2^n)

一个语法点:想用全局变量,可以直接设置类的成员变量,不应该像 java 那样用 static 静态变量!这一点很容易被忽视!

class Solution {
private:
    int max;
    
    int f(vector<int>& nums, int n) {
        if(n < 1) return n;

        int temp = 0, maxCurEnd = 1;

        for(int i = 1; i < n; i++) {
            temp = f(nums, i);
            if(nums[i-1] < nums[n-1] && temp+1 > maxCurEnd) {
                maxCurEnd = temp + 1;
            }
        }

        if(max < maxCurEnd) {
            max = maxCurEnd;
        }

        return maxCurEnd;
    }
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty()) return 0;

        max = 0;
        f(nums, nums.size());
        return max;
    }
};

第二种方法:仍然是递归,但是加入哈希表存储,是记忆化递归

程序和之前基本一样,就是加入了哈希表存储已有的结果,在递归函数内部加上判断。

通过记忆化的操作,我们把时间复杂度从 O(2^n) 降低到了 O(n^2)

这种将问题规模不断减少的做法,被称为自顶向下的方法。但是,由于有了递归的存在,程序运行时对堆栈的消耗以及处理是很慢的,在实际工作中并不推荐。更好的办法是自底向上。

class Solution {
private:
    int max;
    unordered_map<int, int> dict;

    int f(vector<int>& nums, int n) {

        if(dict.count(n)) {
            //return dict.find(n);
            return dict[n];
        }

        if(n < 1) return n;

        int temp = 0, maxCurEnd = 1;

        for(int i = 1; i < n; i++) {
            temp = f(nums, i);
            if(nums[i-1] < nums[n-1] && temp+1 > maxCurEnd) {
                maxCurEnd = temp + 1;
            }
        }

        if(max < maxCurEnd) {
            max = maxCurEnd;
        }

        dict.insert(pair<int, int>(n, maxCurEnd));
        return maxCurEnd;
    }
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty()) return 0;

        max = 0;
        f(nums, nums.size());
        return max;
    }
};

第三种方法(推荐):Bottom-Up解法

最简单的一维动态规划问题,用一个数组dp存放每一个子问题的值

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty()) return 0;

        vector<int> dp(nums.size(), 1); //一维数组用来存储0 - n-1范围内,以该位置数为结尾的最长上升子序列的长度
        int max = 1;

        for(int i = 0; i < nums.size(); i++) { //循环遍历更新dp所有值
            for(int j = 0; j < i; j++) {
                if(nums[j] < nums[i] && dp[j]+1 > dp[i]) {
                    dp[i] = dp[j] + 1;
                }
            }
            if(max < dp[i]) {
                max = dp[i];
            } 
        }
        return max;
    }
};

第四种方法(最优):采用动态规划+二分法的方法,参考题解

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.empty()) return 0;

        vector<int> tails(nums.size(), 0); //严格递增序列
        int res = 0;

        for(int num : nums) { //循环遍历整个nums
            int i = 0, j = res;
            while(i < j) {
                int mid = i + (j-i)/2;
                if(tails[mid] < num) i = mid + 1;
                else j = mid;
            }
            //i==j
            tails[i] = num;
            if(j == res) res += 1;
        }
        return res;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值