题目:最长上升子序列
- 给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是[2,3,7,101],它的长度是 4。
说明:
- 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
- 你算法的时间复杂度应该为
O(n^2)
。 - 进阶: 你能将算法的时间复杂度降低到
O(n logn)
吗?
来源:力扣(LeetCode)第300题
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
分析:
- 非常好的一道题目
- 可以使用动态规划来解决。
- 除了最常规的一种方法外还有另一种动态规划的方法,使用二分查找的思想。
- 具体可以看看搜索插入位置(LeetCode第35题)
- 动态规划:
- 设1个状态表示到这个数为止,最长上升子序列是多少。
- 然后遍历数组,当遍历到第i个状态时,查看在dp中到第i个数为止已经有多长了,那么你只要在右边找到比当前数大的值,然后在它的dp数组下加上1。
- 最后找到dp数组中最大的值,就是答案。
- dp + 二分查找:
- 我们遍历数组,将每个数放进dp数组中。
- 由于dp数组是单调递增的,所以可以使用二分查找找到插入的位置。
- 有两种情况,如果找到了这个值,那么直接覆盖就行了,如果没找到这个值,那么将比它大的最小的值覆盖。其实就是插入位置。
代码:
dp
class Solution { public int lengthOfLIS(int[] nums) { int len = nums.length; int[] dp = new int[len]; Arrays.fill(dp, 1); for (int i = 0; i < len; i++) { for (int j = i + 1; j < len; j++) { if (nums[i] < nums[j]) dp[j] = Math.max(dp[j], dp[i] + 1); } } int ans = 0; for (int i = 0; i < len; i++) ans = Math.max(ans, dp[i]); return ans; } }
dp + 二分
class Solution { private int len; private int[] dp; public int lengthOfLIS(int[] nums) { len = nums.length; dp = new int[len]; Arrays.fill(dp, Integer.MAX_VALUE); for (int i = 0; i <len; i++) dp[find(nums[i])] = nums[i]; int ans = 0; for (int i = 0; i <len; i++) {if (dp[i] != Integer.MAX_VALUE) ++ans;} return ans; } public int find(int val) { int left = 0, right = len - 1; while (left <= right) { int mid = right + (left - right >> 1); if (dp[mid] == val) { return mid; } else if (dp[mid] < val) { left = mid + 1; } else { right = mid - 1; } } return left; } }