LeetCode 300. Longest Increasing Subsequence(最长递增子序列)

问题描述

  • Given an unsorted array of integers, find the length of longest increasing subsequence.
    For example,
    Given [10, 9, 2, 5, 3, 7, 101, 18],
    The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.
  • Your algorithm should run in O(n^2) complexity.
  • Follow up: Could you improve it to O(n log n) time complexity?
  • 地址

问题分析

  • 方法1 :暴力递归
    如果用暴力递归的话,若求 [i ~ end] 的最长上升子序列的长度,对nums[i]有要或不要两种决策,如果nums[i] 大于上一个值,那么这两种决策中的最大值便即为所求。如果改写动态规划的话,有种最优子结构的意思。
    时间复杂度 :O(2^N)
  • 方法2 :记忆化搜索
    可以用记忆化搜索来优化上述递归过程,为了节省空间,可以用 preIndex 来代替递归中 preValue的作用。
    时间复杂度:O(N^2)
  • 方法3 :动态规划
    如果换一个思路,用子数组那种经典套路,求以某一位置结尾或者开头的最优解,然后最终的最优解一定在上述最优中选优。
    length[i] 表示必须以nums[i]结束的最长子序列的长度。那么可以根据length[0],length[1]...length[i - 1]来得到 length[i],具体见实现。最终最长子序列的长度一定是length[i]中的最大值
    当然,该题也可以用length[i] 表示必须以nums[i]开头的最长子序列的长度,从右向左更新即可。
    时间复杂度:O(N^2)
  • 方法4 :二分查找法
    首先明确二分查找的特点:如果待查找元素 num 不在已排序数组中,那么查找结束后,最终left停在刚刚大于num的位置,right 停在刚刚小于 num 的位置。
    然后过程是这样的:
    这里写图片描述

  • 对于以上四种方法,LeetCode discuss 中都有提到。

经验教训

  • 对比方法1与方法3来看,如果我们状态选取的不一样,最终算法时间复杂度也不一样*最优子结构子数组常用套路*的一种决策,
  • 二分查找后leftright的性质。

代码实现

  • 暴力递归(TLE)
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        return maxLenOfLIS(nums, 0, Integer.MIN_VALUE);
    }
    //返回[i ~ end] 的最长递增子序列:要不要nums[i]
    public int maxLenOfLIS(int[] nums, int i, int preValue) {
        if (i== nums.length) {
            return 0;
        }
        int maxLen = 0;
        if (nums[i] > preValue) {
            maxLen = 1 + maxLenOfLIS(nums, i + 1, nums[i]);
        }
        maxLen = Math.max(maxLen, maxLenOfLIS(nums, i + 1, preValue));
        return maxLen;
    }
  • 记忆化搜索
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int[][] memory = new int[nums.length][nums.length + 1];
        return maxLenOfLIS(nums, 0, -1, memory);
    }

    public int maxLenOfLIS(int[] nums, int i, int preIndex,int[][] memory) {
        if (i == nums.length) {
            return 0;
        }
        if (memory[i][preIndex + 1] > 0) {
            return memory[i][preIndex + 1];
        }
        int maxLen = 0;
        if (preIndex == -1 || nums[i] > nums[preIndex] ) {
            maxLen = 1 + maxLenOfLIS(nums, i + 1, i, memory);
        }
        maxLen = Math.max(maxLen, maxLenOfLIS(nums, i + 1, preIndex, memory));
        memory[i][preIndex + 1] = maxLen;
        return maxLen;
    }
  • 动态规划
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int[] length = new int[nums.length];
        int maxLen = 0;
        //length[i] :表示必须以nums[i]结束的最长子序列的长度
        for (int i = 0; i < nums.length; ++i) {
            //初始化为 1
            length[i] = 1;
            for (int j = 0; j < i; ++j) {
                //遍历前面元素,看能否更新length[i]
                if (nums[i] > nums[j]) {
                    length[i] = Math.max(length[i], length[j] + 1);
                }
            }
            //更新最大长度
            maxLen = Math.max(length[i], maxLen);
        }
        return maxLen;
    }
  • 二分查找
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int[] lis = new int[nums.length];
        //LIS的长度
        int len = 0;
        for (int num : nums) {
            //利用二分法,最终left停在刚好比num大的位置
            int left = 0;
            int right = len - 1;
            while(left <= right) {
                int mid = left + (right - left) / 2;
                if (num > lis[mid]) {
                    left = mid + 1;
                }else {
                    right = mid - 1;
                }
            }
            //用num替代该元素
            lis[left] = num;
            //看是否是新增加了元素,若是,则更新len
            len = left == len ? len + 1 : len; 
        }
        return len;
    }

猜你喜欢

转载自blog.csdn.net/zjxxyz123/article/details/80273241