leetcode-最长上升子序列
题目描述:
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
示例:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
题解:
思路1:动态规划,dp[i] 表示以 nums[i] 结尾的最长上升子序列
确定 dp[i] :若 nums[i] 大于它位置之前的某个数( 如nums[j] ),那么 nums[i] 就可以接在后面,dp[i] = dp[j] + 1,确定满足条件的最大值取为dp[i]即可
每次做个判断:dp[i] = dp[i] > (dp[j] +1) ? dp[i] : (dp[j] + 1);
代码:
public int lengthOfLIS(int[] nums) { int len = nums.length; int[] dp = new int[len]; Arrays.fill(dp,1); int res = 1; for(int i = 1; i < len; i++) { for(int j = 0; j < i; j++){ if(nums[i] > nums[j]){ dp[i] = dp[i] > (dp[j] +1) ? dp[i] : (dp[j] + 1); } } res = res > dp[i] ? res : dp[i]; } return res; }
思路2:动态规划+贪心算法+二分查找,dp[i] 表示长度为 i+1 的所有上升子序列的结尾的最小值,dp[i]为上升数组
遍历:如果nums[i] 严格大于dp中现有的最后一个元素,则直接添加进去,后续若遍历到比这个数更小的且满足条件的数,到时再替换;
否则,在dp中寻找第一个大于nums[i]的数(二分),将dp中的数用nums[i]替换,得到一个更小的数:
(分析:若第一个大于nums[i] 的数为dp[2],则说明dp[1]是小于nums[i]的,说明dp[i]<nums[i]<dp[2],那么nums[i]就可以接在dp[1]后面形成一个长度为3的序列,从而nums[i]即可代替dp[2];)
代码:
public int lengthOfLIS(int[] nums) { int len = nums.length; if(len<=1){ return len; } int[] dp = new int[len]; dp[0] = nums[0]; int end = 0; for(int i = 1; i<len;i++){ if(nums[i] > dp[end]){ end++; dp[end] = nums [i]; }else{ int left = 0; int right = end; while(left<right){ int mid = left + ((right - left)/2); if (dp[mid] < nums[i]){ left = mid +1; }else{ right = mid; } } dp[left] = nums[i]; } } end++; return end;