300. Longest Increasing Subsequence
题目:小猴子下山,沿着下山的路有一排桃树,每棵树都结了一些桃子。小猴子想摘桃子,但是又一些条件需要遵守,小瘦子只能沿着下山的方向走,不能回头,每棵树最多摘一个,而且一旦摘了一棵树的桃子,就不能再摘比这棵树结的桃子少的树上的桃子,那么小猴子最多能摘到几课桃子呢?
距离说明,比如有五棵树,分别结了10,4,5,12,8棵桃子,那么小猴子最多能摘3颗桃子,来自于结了4,5,12颗桃子的桃树。
感谢futurehau大神优秀的解析:链接
O(nlog(n))
算法流程:
使用一个数组h,首先令h[0]=arr[0]。记已经赋值了的h前部分为有序区,我们只考察有序区。
• 往后遍历,对于arr[i],在h的有序区中寻找第一个大于arr[i]的位置。如果找到,就把那个位置的值更新为arr[i],否则h的有序区长度增一,并且新增位置的值就为arr[i]。使用二分查找位置
•上述过程中,从位置0到,arr[i]的更新位置的元素个数就是以arr[i]结尾的最长递增子序列的长度。从二分查找出的位置就可以知道这个长度。使用一个全局变量来max存储更新最长递增子序列的长度。
算法原理:
•h[i]表示遍历到当前时刻为止,长度为i+1的最长递增子序列的最小末尾。这样其实我们每次所做的工作就是要么增加了最长递增子序列的长度,要么就是长度不变,但是更新了每个长度对应的最小末尾,而这有利于之后扩展长度,因为你更小嘛,我后半的元素更容易比你大。
•这样其实最后的h的有效区长度即为所求,但是为了不再去遍历,中途使用一个max来记录当前位置时的最长递增子序列,更新max即可。做了n次,每次二分查找位置log(n),所以复杂度为n(logn)。
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0)
return 0;
int[] h=new int[nums.length];
h[0]=nums[0];
int max=0;//最长子序列最右边的位置
for(int i=1;i<nums.length;i++){
if(nums[i]>h[max]){
h[++max]=nums[i];
continue;
}
else{
int pos=findFirstBigger(h,0,max,nums[i]);
h[pos]=nums[i];
}
}
return max+1;
}
public int findFirstBigger(int[] h,int left,int right,int target){
if(left==right)
return left;
int mid=(left+right)/2;
if(h[mid]<target)
return findFirstBigger(h,mid+1,right,target);
else
return findFirstBigger(h,left,mid,target);
}