Longest Increasing Subsequence的两种解法
问题描述:
给出一个未排序队列nums,如[10, 9, 2, 5, 3, 7, 101, 18]。找出其中最长的增长序列,但不是连续增长序列,如[2, 3, 7, 101]就是对应的最长增长序列LIS,因为序列不唯一,所以要求返回的是长度,如4.
一.动态规划 O(n^2):
比较容易想到的就是复杂度为O(n^2)的算法。这是一个备忘录算法,也是动态规划算法。需要建立一个备忘录dp,备忘录dp[i]记录序列从下标0到下标i最长的子序列长度。对于dp[j]的值则需要在nums序列红中找到0到(j-1)所有比nums[j]小元素,并在这些元素中选择备忘录dp值最大的一个如dp[k],则dp[j]=dp[k]+1;
public int lengthOfLIS(int[] nums) { if(nums==null || nums.length==0) return 0; int[] bigLength=new int[nums.length]; for(int i=0;i<nums.length;i++) bigLength[i]=1; int maxLength=1; for(int i=1;i<nums.length;i++){ //从前面找到比 nums[i]小,且dp值最大的那个。加1便是当前的值 for(int j=0;j<i;j++){ if(nums[j]<nums[i]) bigLength[i]=Math.max(bigLength[j]+1,bigLength[i]); } maxLength=Math.max(bigLength[i],maxLength); } return maxLength; }
二 .二分法查找O(nlg(n))
首先建立一个栈stack来存储遍历到当前时刻 i 的一个最长递增序列(栈内是递增序列,但概念和题目中的递增序列不同)。设当前时刻为 i 则
1.如果元素 i 比栈顶元素大则入栈,stack[top++]=nums[i+1];
2.如果元素 i 比栈顶元素小,则在栈中采用二分查找法找到一个位置j 替换成当前元素nums[i] 。 该做法的目的是如果出现小元素就往栈内部替换,当前替换的结果影响下一次的替换,特别是栈顶元素。栈顶元素的替换需要比较stack[top],stack[top--]及nums[i]三个的值。
3.最后输出 栈的长度。
public int lengthOfLIS(int[] nums) { if(nums==null || nums.length==0) return 0; int[] stack= new int[nums.length]; int top=0; for(int num:nums){ if(top==0 || stack[top-1]<num) stack[top++]=num; else{ //如果在栈中没有对应的元素,则将找到的插入坐标为 j 返回-j-1. 如果找到则返回对应的坐标位置。 int i=Arrays.binarySearch(dp,0,top,num); i= i<0? -i-1:i; dp[i]=num; } } return top; }
另外网上有关于只用栈没用利用二分法查找法的做法,使得复杂度变为O(n),试了下是不行的。他大概的思路是:
1.如果当前栈为空或栈顶元素小于当前元素nums[i],则入栈
2.如果nums[i]<stack[top] 且 nums[i]>stack[top-1] 则替换栈顶元素,stack[top]=nums[i]。
这种做法忽略了stack[top-1]之前的元素对stack[top-1]的影响。算法代码如下:
public int lengthOfLIS(int[] nums) { int len=nums.length; Stack<Integer> stack=new Stack<Integer>(); for(int i=len-1; i>=0; i--) { if(stack.isEmpty()) { stack.push(nums[i]); }else { int val= stack.pop(); if(stack.isEmpty()) { if(nums[i]>=val) { val=nums[i]; }else { stack.push(val); val=nums[i]; } }else { int up=stack.peek(); if(nums[i]<val) { stack.push(val); val=nums[i]; }else if(nums[i]>val && nums[i]<up) { val=nums[i]; } } stack.push(val); } } return stack.size(); }