- 题目:最长子序列
- 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
- 子序列可以不连续,但是得按原数组中的相对位置关系
- 示例 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 - 进阶:
- 你可以设计时间复杂度为 O(n2) 的解决方案吗?
你能将算法的时间复杂度降低到 O(n log(n)) 吗?
- 思路:
- 先得把[[数组]]规定以哪个位置结尾划分子阶段。以0~i位置划分
- 得到子数组(子阶段)后再进行判断每个阶段内最长子序列
- 解法一:O(n^2)
- 遍历左边比arr(i)小的值,dp()+1就是i的子序列
- 解法二:O(N*logN)
- 思路:遍历的复杂度n省不了,可以省每个位置向前查询时把前面所有遍历一遍的n
- 要省略向前查询的n需要利用之前的阶段产生结果从而不用遍历
- 操作:创建一个辅助数组(end),对于i位的数,提供i之前最长子序列长度为0~有效值之间的最小结尾。所以只要i位的数比某个最小结尾大,则i位的最长子序列的长度为那个最小结尾+1 。
- 如何构建辅助数组:
- 操作:i位置先在end数组里找到最靠右(即最大)且比i位置小的数。往右一格填上i的值
- 思想:对于i位置来说,有用的只有每个长度的最长子序列的结尾数。之前数据的排列顺序无用。
- 因为假设之前已经存在了每个长度的最长子序列的结尾数,所以找到比某个结尾数大,i的长度是其+1。
- 因为子序列的结尾数一定和最长长度正相关。所以可以用二分法来加快查询每个长度的最长子序列的结尾数的过程
- 代码:
-
(- // 本题测试链接 : https://leetcode.com/problems/longest-increasing-subsequence public class Code03_LIS { public static int lengthOfLIS(int[] arr) { if (arr == null || arr.length == 0) { return 0; } int[] ends = new int[arr.length]; ends[0] = arr[0]; int right = 0; int l = 0; int r = 0; int m = 0; int max = 1; for (int i = 1; i < arr.length; i++) { l = 0; r = right; while (l <= r) {//二分的过程 m = (l + r) / 2; if (arr[i] > ends[m]) { l = m + 1; } else { r = m - 1; } } right = Math.max(right, l);//最大的有效值 ends[l] = arr[i]; max = Math.max(max, l + 1); } return max; } })