Longest Increasing Subsequence
2015-11-20 11:45
337 查看
Given an unsorted array of integers, find the length of longest increasing subsequence.
For example,
Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
思路: 题目的意思是求数组中递增序列的最大长度,序列是可以不连续的。用f(i)表示第0~i个数中递增序列的最大长度。 对于每一个nums[i]要求f(i) 的话,需要找出0~(i-1) 中所有小于nums[i] 的数nums[j],其中 0 <= j <=(i-1),记录下最大的f(j) ,记为max ,那么f(i)=max+1 ,否则找不到比nums[i]还要小的数,f(i)=1,并在整个遍历数组的过程中记录递增序列的最大长度res。
对于数组nums = [10, 9, 2, 5, 3, 7, 101, 18] ,res表示所要求的最大递增序列的长度。
初始值 f(0) =1 ,更新res=1
对于 i =1 , nums[1]=9 , 在1之前没有小于nums [i] 的数 ,所以f(1)=1, res=1
对于i=2 ,nums[2]=2, 在2之前没有小于nums[i]的数,所以f(2)=1,res=1
对于i=3 ,nums[3]=5 ,在3之前小于5的数有2 ,max=f(2)=1,所以f(5)=max+1=2, 此时更新res=2
对于i=4,nums[4]=3 ,在4之前小于3的数有 2,f(3)=f(2)+1=2, 此时res=2
对于i=5,nums[5]=7,在 5之前小于7的数有2,3,5 ,而f(2)=1, f(3)=2,f(5)=2 , max是f(2)、f(3)和f(5)中最大的一个,max=2,则f(7)=3,此时更新res=3
对于i=6,nums[i]=101, 在6之前小于101的有f(0),f(1),f(2),f(3),f(4),f(5),它们之中最大的为3,则max=3,所以f(6)=max+1=4,此时更新res=4
同理,对于i=7 ,有f(7)=4 ,此时res=4
最后最长递增子序列的长度为res=4
算法如下,但是时间复杂度为o(n^2)。
改进: 想到是查找算法,上述算法所用的是线性查找,比线性查找更加快的是二分查找算法,但是二分查找算法需要每次可以折半,如果可以查找一个有序的序列那就好了。 我们可以用一个数组len来记录对应长度的序列末尾最小的一个数字,这话说起来比较绕口,举个例子
比如len[3] 表示 长度为3的序列中末尾数字中最小的一个数
有如下序列:
3,1,2,6,4,-1,5,8,9
比如在数字5之前,长度为3的递增序列有 1,2,6 和1,2,4 这两个序列的末尾数字分别为6和4,其中4比6小,所以记录长度为3的递增序列的末尾数字为len[3]=4
这样,可以在遍历到 数组之下标为s的时候 ,在len[1]至 len[s]中找到一个比nums[s]还要小的数r,r对应的长度为索引 idx,更新f(s)=idx+1 ,如果len[idx+1]大于nums[s],更新len[idx+1]=nums[s]
For example,
Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
思路: 题目的意思是求数组中递增序列的最大长度,序列是可以不连续的。用f(i)表示第0~i个数中递增序列的最大长度。 对于每一个nums[i]要求f(i) 的话,需要找出0~(i-1) 中所有小于nums[i] 的数nums[j],其中 0 <= j <=(i-1),记录下最大的f(j) ,记为max ,那么f(i)=max+1 ,否则找不到比nums[i]还要小的数,f(i)=1,并在整个遍历数组的过程中记录递增序列的最大长度res。
对于数组nums = [10, 9, 2, 5, 3, 7, 101, 18] ,res表示所要求的最大递增序列的长度。
初始值 f(0) =1 ,更新res=1
对于 i =1 , nums[1]=9 , 在1之前没有小于nums [i] 的数 ,所以f(1)=1, res=1
对于i=2 ,nums[2]=2, 在2之前没有小于nums[i]的数,所以f(2)=1,res=1
对于i=3 ,nums[3]=5 ,在3之前小于5的数有2 ,max=f(2)=1,所以f(5)=max+1=2, 此时更新res=2
对于i=4,nums[4]=3 ,在4之前小于3的数有 2,f(3)=f(2)+1=2, 此时res=2
对于i=5,nums[5]=7,在 5之前小于7的数有2,3,5 ,而f(2)=1, f(3)=2,f(5)=2 , max是f(2)、f(3)和f(5)中最大的一个,max=2,则f(7)=3,此时更新res=3
对于i=6,nums[i]=101, 在6之前小于101的有f(0),f(1),f(2),f(3),f(4),f(5),它们之中最大的为3,则max=3,所以f(6)=max+1=4,此时更新res=4
同理,对于i=7 ,有f(7)=4 ,此时res=4
最后最长递增子序列的长度为res=4
算法如下,但是时间复杂度为o(n^2)。
public class Solution { public int lengthOfLIS(int[] nums) { int res=0; int n=nums.length; if(n==0) return 0; int[] f=new int ; f[0]=1; res=1; for(int i=1;i<n;i++) { int max=Integer.MIN_VALUE; for(int j=i-1;j>=0;j--) { if(nums[j]<nums[i]) max= max > f[j] ? max : f[j]; } f[i]=( max!=Integer.MIN_VALUE ? max+1 : 1); res= res > f[i] ? res : f[i] ; } return res; } }
改进: 想到是查找算法,上述算法所用的是线性查找,比线性查找更加快的是二分查找算法,但是二分查找算法需要每次可以折半,如果可以查找一个有序的序列那就好了。 我们可以用一个数组len来记录对应长度的序列末尾最小的一个数字,这话说起来比较绕口,举个例子
比如len[3] 表示 长度为3的序列中末尾数字中最小的一个数
有如下序列:
3,1,2,6,4,-1,5,8,9
比如在数字5之前,长度为3的递增序列有 1,2,6 和1,2,4 这两个序列的末尾数字分别为6和4,其中4比6小,所以记录长度为3的递增序列的末尾数字为len[3]=4
容易证明当 i <j 的时候有 len[i] < len[j] , 假设j=i+k , i<j =i+k len[i]=m ,len[i+k] =n 如果m>n 那就说明长度为i的序列的最后一个数字大于长度为i+k的最后一个数字,这个不合理的,m在n的后面,到n的时候序列的长度已经可以是i+k了,到m的时候长度肯定不止i+k了,所以len[i]=m是不合理的,所以一定有 i<j ,就有len[i] < len[j]
这样,可以在遍历到 数组之下标为s的时候 ,在len[1]至 len[s]中找到一个比nums[s]还要小的数r,r对应的长度为索引 idx,更新f(s)=idx+1 ,如果len[idx+1]大于nums[s],更新len[idx+1]=nums[s]
public class Solution { public int lengthOfLIS(int[] nums) { int res=0; int n=nums.length; if(n==0) return 0; int[] f=new int ; f[0]=1; int[] len=new int[n+1]; len[1]=nums[0]; for(int i=1;i<n;i++) { //二分查找len中小于nums[i]的数 int j=i; while(j>=1 && len[j]==0) j--; int right=j; int left=1; while(left<=right) { int mid=left+(right-left)/2; if(len[mid]>=nums[i]) { right=mid-1; }else left=mid+1; } if(right<left) f[i]= right+1; if(len[f[i]]!=0) len[f[i]]= len[f[i]] < nums[i] ? len[f[i]] : nums[i]; else len[f[i]]=nums[i]; res=res > f[i] ? res : f[i]; } return res!=0 ? res : 1; } }
相关文章推荐
- Ant常用命令简介以及一个最完整的build.xml样例
- Bluetooth 4.0之Android 讲解
- 蓝牙安全特性
- UIDeviceOrientation UIInterfaceOrientation 区别
- leetcode 303 : Range Sum Query - Immutable
- 使用Java的BlockingQueue实现生产者-消费者
- UIScrollView (API+循环滚动)
- iOS 常用:点击屏幕和return退出隐藏键盘和解决虚拟键盘挡住UITextField的方法
- leetcode@ [307] Range Sum Query - Mutable / 线段树模板
- To be continued? No, it's over.
- request.getContextPath获取绝对路径
- iOS-----使用AddressBookUI管理联系人
- getRequestDispatcher()与sendRedirect()的区别
- 让UITableView自动滑动(定位)到某一行cell section刷新 cell刷新
- serialVersionUID的作用
- UIViewContentMode各模式的含义和效果
- 教大家一个在自定义的View里面不写block,delegate,Notification快速刷新UI的方法!
- 动手搭建 iOS CI 环境之「了解 xcodebuild 命令」
- UIL doesn't support scheme(protocol) by default 问题
- sequelpro-mac上连接mysql的免费工具