每日一题 300最长递增子序列(贪心+二分)(灵神模版)

题目

LIS
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

提示:

1 <= nums.length <= 2500
-104 <= nums[i] <= 104

题解

子集型回溯

回溯三问

  1. 当前问题:以nums[i]结尾的LIS
  2. 当前操作:枚举nums[j]
  3. 下一个子问题:以nums[j]结尾的LIS
//记忆化搜索
class Solution {
    private int[] nums, cache;

    public int lengthOfLIS(int[] nums) {
        this.nums = nums;
        int n = nums.length;
        cache = new int[n];
        int res = 0;
        for (int i = 0; i < n; i++) {
            res = Math.max(res, dfs(i));
        }
        return res;
    }

    private int dfs(int i) {
        if (cache[i] > 0) {
            return cache[i];
        }
        for (int j = 0; j < i; j++) {
            if (nums[j] < nums[i]) {
                cache[i] = Math.max(cache[i], dfs(j));
            }
        }
        //最后需要加上nums[i],因此需要+1
        return ++cache[i];
    }
}

递推

//递推
class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        int[] f = new int[n];
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    f[i] = Math.max(f[i], f[j]);
                }
            }
            res = Math.max(res, ++f[i]);
        }
        return res;
    }
}

贪心+二分

g[i]表示长度为i+1的IS(递增子序列)的最小值
例如,nums=[1,6,7,2,4,5,3]
nums = 1
nums = 1 6
nums = 1 6 7
nums = 1 2 7
nums = 1 2 4
nums = 1 2 4 5
nums = 1 2 3 5

class Solution {
   public int lengthOfLIS(int[] nums) {
       List<Integer> g = new ArrayList<>();
       for (int x : nums) {
           int j = lowerBound(g, x);
           if (j == g.size()) {
               g.add(x);
           } else {
               g.set(j, x);
           }
       }
       return g.size();
   }

   public int lowerBound(List<Integer> g, int target) {
       int left = 0, right = g.size() - 1;
       while (left <= right) {
           int mid = (right - left) / 2 + left;
           if (g.get(mid) < target) {
               left = mid + 1;
           } else {
               right = mid - 1;
           }
       }
       return left;
   }
}

空间优化

class Solution {
    public int lengthOfLIS(int[] nums) {
        int ng = 0;
        for (int x : nums) {
            int j = lowerBound(nums, ng - 1, x);
            nums[j] = x;
            if (j == ng) {
                ng++;
            } 
        }
        return ng;
    }

    public int lowerBound(int nums[], int right, int target) {
        int left = 0;
        while (left <= right) {
            int mid = (right - left) / 2 + left;
            if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return left;
    }
}

你可能感兴趣的:(算法,java,leetcode,笔记)