LC 300. 最长递增子序列

 

class Solution {
    /**
    暴力法 + dp
    思路: 
        dp[i] 表示, 必须以第 i 个位置结束的最长递增子序列的长度
            每到一个位置, 都要回头扫描一遍已经遍历过数组, 如果存在
            比当前位置的数值小的数组, 从他的 dp 中获取它的长度 + 1
            可以得到 dp[i] 位置当前的一个可能取值, 去 0...i - 1 
            这段区间中所有可能取值的最大值, 就是 dp[i] 位置的答案, 
            如果 0...i - 1 这段区间的数值都比 nums[i] 大, 那么 
            dp[i] = 1 
     */
    public int lengthOfLIS(int[] nums) {
        int N = nums.length;
        if (N == 1) return 1;   // 如果数组长度位置, 那么最长递增子序列就是 1
        // dp[i] 表示, 必须以第 i 个位置结束的最长递增子序列的长度
        int[] dp = new int[N];
        dp[0] = 1;  // dp[0] 必为 1
        int ans = 1;
        for (int i = 1; i < N; i++) { 
            dp[i] = 1;
            // 扫描 0...i-1 区域的可能取值
            for (int j = i - 1; j >= 0; j--) {
                if (nums[i] > nums[j])  // 找到一个可能取值
                    // 取最大的那个
                    dp[i] = Math.max(dp[j] + 1, dp[i]);
            }
            ans = Math.max(ans, dp[i]);
        }
        return ans;
    }
}
class Solution {
    /**
    二分 + dp
    思路: 
        dp 数组含义, 到达 i 位置的最长递增子序列是谁
            先在 dp 数组的有效区域找到第一个比 当前数值 nums[i] 大的
        用 nums[i] 去更新这个数; 找不到就追加在最后, size++
        比如:    [10,9,2,5,3,7,101,18]
        i == 0, 10  --> size = 1, dp = [10,0....]      // 0....表示无效区域
        i == 1, 9   --> size = 1, dp = [9,0....]
        i == 2, 2   --> size = 1, dp = [2,0....]
        i == 3, 5   --> size = 2, dp = [2,5,0...]       
        i == 4, 3   --> size = 2, dp = [2,3,0...]
        i == 5, 7   --> size = 3, dp = [2,3,7,0...]
        i == 6, 101 --> size = 4, dp = [2,3,7,101,0...]
        i == 7, 18  --> size = 4, dp = [2,3,7,18,0...]

        因为 dp 数组是有序, 所以我们能通过二分查找快速确定要找的位置
        整体时间复杂度 O(n * log n)
     */

    public int lengthOfLIS(int[] nums) {
        int N = nums.length;
        if (N == 1) return 1;
        int[] dp = new int[N];
        int size = 1;

        dp[0] = nums[0];
        for (int i = 1; i < N; i++) {
            // 找 dp 数组中第一个比 nums[i] 大
            int mostLeftBigIndex = search(dp, size, nums[i]);
            if (mostLeftBigIndex == -1) {
                // 如果找不到, 在后面追加
                dp[size++] = nums[i];
            } else {
                // 找到了, 用当前位置的值, 更新 dp 的对应位置
                dp[mostLeftBigIndex] = nums[i];
            }
        }
        return size;
    }
    // 二分查找, 找数组中第一个比 key 大
    private int search(int[] dp, int size, int key) {
        int l = 0, r = size - 1;
        int res = -1;
        while (l <= r) {
            int m = (l + r) / 2;
            if (dp[m] >= key) {
                res = m;
                r = m - 1;
            } else {
                l = m + 1;
            }
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值