300. 最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法一:动态规划

class Solution {
    /**
     * 动态规划
     * 1. 状态定义:f(n)=从index=0到index=n中最长的上升子序列
     * 2. 转移方程:f(n)=f(x)+num[n], {f(0), f(1), ..., f(n-1)}过滤最后一个元素小于nums[n],并从中找出最长的上升子序列的f(x)
     *
     * @param nums
     * @return
     */
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int max = 1;
        int[][] fn = new int[nums.length][];
        fn[0] = new int[]{nums[0]};
        for (int i = 1; i < nums.length; i++) {
            int[] beforeMaxLengthOfLIS = findBeforeMaxLengthOfLIS(fn, i, nums[i]);
            int newSize = beforeMaxLengthOfLIS != null ? beforeMaxLengthOfLIS.length + 1 : 1;
            int[] curLIS;
            if (beforeMaxLengthOfLIS == null) {
                curLIS = new int[1];
            } else {
                curLIS = Arrays.copyOf(beforeMaxLengthOfLIS, newSize);
            }
            curLIS[newSize - 1] = nums[i];
            fn[i] = curLIS;
            if (newSize > max) {
                max = newSize;
            }
        }
        return max;
    }

    /**
     * {f(0), f(1), ..., f(n-1)}过滤最后一个元素小于nums[n],并从中找出最长的上升子序列的f(x)
     *
     * @param fn
     * @param curNum
     * @return
     */
    private int[] findBeforeMaxLengthOfLIS(int[][] fn, int endIndex, int curNum) {
        int[] maxLength = null;
        int max = 0;
        for (int i = 0; i < fn.length; i++) {
            int[] list = fn[i];
            if (i >= endIndex || list[list.length - 1] >= curNum) {
                continue;
            }
            if (list.length > max) {
                max = list.length;
                maxLength = list;
            }
        }
        return maxLength;
    }
}
运行效率

解法二:定义一个最长上升子序列数组,只能添加或者更新,最终的长度就是最长的长度

class Solution {
    /**
     * 时间复杂度O(N*logN)
     * 
     * @param nums
     * @return
     */
    public int lengthOfLIS(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        List result = new ArrayList<>();
        result.add(nums[0]);
        for (int i = 1; i < nums.length; i++) {
            // 当前元素大于数组中的所有元素,添加在尾部
            if (result.get(result.size() - 1) < nums[i]) {
                result.add(nums[i]);
                continue;
            }
            // 二分查找,找到一个元素比当前元素大,前一个元素比当前元素小的元素,替换成当前元素
            int begin = 0, end = result.size() - 1, mid;
            do {
                mid = (begin + end) / 2;
                if (nums[i] > result.get(mid)) {
                    begin = mid + 1;
                } else {
                    end = mid;
                }
            } while (begin < end);
            result.set(begin, nums[i]);
        }
        return result.size();
    }

    public static void main(String[] args) {
        int[] nums = {10, 9, 2, 5, 3, 4};
        int result = new Solution().lengthOfLIS(nums);
        System.out.println(result);
    }
}
运行效率

你可能感兴趣的:(300. 最长上升子序列)