一、题目
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
二、思路
1、动态规划
本题属于较为经典的动态规划,设立数组dp,dp[i]为以nums[i]结尾的递增子序列最长值。状态转移方程如下:
dp[i]的初始值为1.
2、贪心+二分
想让递增子序列最长,则需要让该序列尽可能增长最慢。
使用一个无序递增数组(无序指不按照nums中的序列,多个递增序列都放在一个数组中,这点下面会解释)来记录最长递增子序列。设定一个数组memo:
1)如果nums[i]>memo[memo.size()-1],则在memo末尾添加nums[i],代表递增子序列长度+1 2)如果nums[i]<=memo[memo.size()-1],说明nums[i]不能直接添加进目前的最长递增子序列中,二分查找以它为末尾的最长子序列长度,并覆盖memo中大于等于nums[i]的元素,如果后续会使用nums[i]所在序列,memo中的元素会慢慢被替代,直到长度超过当前的memo.size()。(这部分自己多写几次代码,手推代码去理解)
三、代码
1、动态规划
class Solution {public int lengthOfLIS(int[] nums) {int[] dp = new int[nums.length];int ans = 1;for (int i = 0; i < nums.length; i++) {dp[i] = 1;for (int j = i-1; j >= 0; j--) {if (nums[i] > nums[j]) {dp[i] = Math.max(dp[i], dp[j] + 1);}}ans = Math.max(ans, dp[i]);}return ans;}}
2、贪心+二分
class Solution {public int lengthOfLIS(int[] nums) {List<Integer> memo = new ArrayList();memo.add(nums[0]);for (int i = 1; i < nums.length; i++) {if (nums[i] > memo.get(memo.size()-1)) {memo.add(nums[i]);} else {int loc = find(memo, nums[i]);memo.set(loc, nums[i]);}}return memo.size();}public int find(List<Integer> memo, int num) {int start = 0, end = memo.size();while (start < end) {int mid = (start + end) / 2;if (memo.get(mid) == num) {return mid;} else if (memo.get(mid) > num) {end = mid;} else {start = mid + 1;}}return start;}}
时间复杂度为O(nlogn),空间复杂度为O(n)。
