Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input:[10,9,2,5,3,7,101,18]
Output: 4 Explanation: The longest increasing subsequence is[2,3,7,101]
, therefore the length is4
.
Note:
- 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?
思路1:DP(时间复杂度o(n^2))
Dp[i] 表示以第i个数字为结尾的最长上升子序列的长度。
对于每个数字,枚举前面所有小于自己的数字 j,Dp[i] = max{Dp[j]} + 1. 如果没有比自己小的,Dp[i] = 1;
实现:
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp=new int[nums.length];
int max=0;
for(int i=0;i<dp.length;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(nums[j]<nums[i]){
dp[i]=Math.max(dp[i],dp[j]+1);
}
}
if(dp[i]>max){
max=dp[i];
}
}
return max;
}
}
思路2:二分查找(时间复杂度o(nlogn)
方法一递推关系简单, 代码实现也异常简洁, 唯一的问题是n ^ 2的复杂度在题目给的数据量较大时会超时。
而方法一也不算是最优的解决方法。实际上这个问题还可以用二分来优化。
做法是构造出一个新的有序的DP数列, 用原数列中的数从左到右维护更新新数列。
初始时DP[1] = nums[1], 从i = 2时遍历原数列, 将每个遍历的数与DP数列的末尾进行比较, 如果大于末尾, 则把DP数列长度提1将nums[i]放在DP数列的最后, 如果小于末尾那么替换掉DP数列中比s[i]大的第一个数。
结束后DP数列的长度就是LIS的长度。
从LIS的性质出发,要想得到一个更长的上升序列,该序列前面的数必须尽量的小。
对于序列1,5,8,3,6,7来说,当子序列为1,5,8时,遇到3时,序列已经不能继续变长了。但是,我们可以通过替换,使“整个序列”看上去更小,从而有更大的机会去变长。这样,当替换5-3和替换8-6完成后(此时序列为1,3,6),我们可以在序列末尾添加一个7了。
那为什么复杂度可以是O(NlogN)呢?
关键就在“替换”这一步上,若直接遍历序列替换,每次替换都要O(N)的时间。但是只要我们再次利用LIS的性质——序列是有序的(单调的),就可以用二分查找,在O(logN)的时间内完成一次替换,所以算法的复杂度是O(NlogN)的。
演示:来自https://blog.csdn.net/qq_37555704/article/details/81479623
这是原数组
首先将f[1]更新
然后发现f[1]还是比当前元素大,继续更新
同上:
这里发现2比1大,于是更新了f[2]
同上
这里最后就返回了3
实现:
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0) return 0;
int[] dp=new int[nums.length];
dp[0]=nums[0];
int j=0;
for(int i=1;i<nums.length;i++){
// 如果小于末尾那么替换掉DP数列中比s[i]大的第一个数。
if(nums[i]<=dp[j]){
//去dp数列中寻找比nums[i]大的第一个数,并用nums[i]替换掉该数
int index=binarySearch(dp,0,j, nums[i]);
dp[index]=nums[i];
}else{
//将每个遍历的数与DP数列的末尾进行比较, 如果大于末尾,
//则把DP数列长度提1将s[i]放在DP数列的最后,
dp[++j]=nums[i];
}
}
//已找到连续递增的子序列,输出序列的长度
return j+1;
}
private int binarySearch(int[] arr, int start,int end,int num) {
while(start<end){
int mid=(end-start)/2+start;
if(arr[mid]<num){
start=mid+1;
}else{
end=mid;
}
}
return end;
}
}
实现2:九章算法
public class Solution {
/**
* @param nums: The integer array
* @return: The length of LIS (longest increasing subsequence)
*/
public int longestIncreasingSubsequence(int[] nums) {
int[] minLast = new int[nums.length + 1];
minLast[0] = Integer.MIN_VALUE;
for (int i = 1; i <= nums.length; i++) {
minLast[i] = Integer.MAX_VALUE;
}
for (int i = 0; i < nums.length; i++) {
// find the first number in minLast >= nums[i]
int index = binarySearch(minLast, nums[i]);
minLast[index] = nums[i];
}
for (int i = nums.length; i >= 1; i--) {
if (minLast[i] != Integer.MAX_VALUE) {
return i;
}
}
return 0;
}
// find the first number > num
private int binarySearch(int[] minLast, int num) {
int start = 0, end = minLast.length - 1;
while (start + 1 < end) {
int mid = (end - start) / 2 + start;
if (minLast[mid] < num) {
start = mid;
} else {
end = mid;
}
}
return end;
}
}
---------------------
参考:
原文:https://blog.csdn.net/LxcXingC/article/details/81238008