对于一个数字序列,请设计一个复杂度为O(nlogn)的算法,返回该序列的最长上升子序列的长度,这里的子序列定义为这样一个序列U1,U2...,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。
给定一个数字序列A及序列的长度n,请返回最长上升子序列的长度。
[2,1,4,3,1,5,6],7
返回:4
public class AscentSequence {
public int findLongest(int[] A, int n) {
int length = A.length;
int[] B = new int[length];
B[0] = A[0];
int end = 0;
for (int i = 1; i < length; ++i) {
// 如果当前数比B中最后一个数还大,直接添加
if (A[i] >= B[end]) { B[++end] = A[i]; continue;
}
// 否则,需要先找到替换位置
int pos = findInsertPos(B, A[i], 0, end); B[pos] = A[i];
}
for (int i = 0; i < B.length; ++i) {
System.out.println(B[i]);
}
return end+ 1; }
/**
* 二分查找第一个大于等于n的位置
*/
private int findInsertPos(int[] B, int n, int start, int end) {
while (start < end) {
int mid = start + (end - start) / 2;// 直接使用(high + low) / 2 可能导致溢出
if (B[mid] < n) {
start = mid + 1;
} else if (B[mid] > n) {
end = mid ;
} else {
return mid;
}
}
return start;
}
}
class AscentSequence {
public:
int findLongest(vector<int> A, int n) {
int dp[1000];
for(int i=0;i<n;i++){
dp[i]=1;
}
for(int i=1;i<n;i++){
int tmax=1;
for(int j=0;j<i;j++){
if(A[j]<A[i]){
tmax=max(tmax,dp[j]+1);
}
}
dp[i]=tmax;
}
int ans=1;
for(int i=0;i<n;i++){
ans=max(ans,dp[i]);
}
return ans;
}
};
import java.util.*;
public class AscentSequence {
public int findLongest(int[] A, int n) {
int[] dp=new int[n];
int max=0;
for(int i=0;i<n;i++){
dp[i]=1;
for(int j=0;j<i;j++){
if(A[i]>A[j]){
dp[i]=Math.max(dp[i],dp[j]+1);
if(dp[i]>max)
max=dp[i];
}
}
}
return max;
}
} public int findLongest(int[] A, int n) {
int[] f = new int[n];//用于存放f(i)值;
f[0]=1;//以第a1为末元素的最长递增子序列长度为1;
for(int i = 1;i<n;i++)//循环n-1次
{
f[i]=1;//f[i]的最小值为1;
for(int j=0;j<i;j++)//循环i 次
{
if(A[j]<A[i]&&f[j]>f[i]-1)
f[i]=f[j]+1;//更新f[i]的值。
}
}
Arrays.sort(f);
return f[n-1];
}
import java.util.*;
public class AscentSequence {
public int findLongest(int[] A, int n) {
int[] B = new int[n+1]; B[1] = A[0];
int len=1,start=0,end=len,mid;
for(int i = 1;i<n;i++){
if(A[i]>B[len]) {len++;B[len] = A[i];}
else{
start=1;end=len;
while(start<=end){
mid=(start+end)/2;
if(B[mid]<A[i]) start=mid+1;
else end=mid-1;
} B[start] = A[i];
}
}
return len;
}
}
原文地址:https://blog.csdn.net/cooper20/article/details/80765897
共有四种思路
### 方法 1 暴力搜索(超出时间限制)
算法
最简单的方法是尝试寻找所有递增子序列,然后返回递增子序列的最大长度。为此,我们使用递归函数lengthofLIS,该函数返回从当前元素(curpos)向前(包括当前元素)的LIS长度。每次函数调用时,考虑两种情况:
public class Solution {
public int lengthOfLIS(int[] nums) {
return lengthofLIS(nums, Integer.MIN_VALUE, 0);
}
public int lengthofLIS(int[] nums, int prev, int curpos) {
if (curpos == nums.length) {
return 0;
}
int taken = 0;
if (nums[curpos] > prev) {
taken = 1 + lengthofLIS(nums, nums[curpos], curpos + 1);
}
int nottaken = lengthofLIS(nums, prev, curpos + 1);
return Math.max(taken, nottaken);
}
} 复杂度分析
算法
在上一个方法中,许多参数相同的递归过程被多次调用。通过将递归调用结果保存在一个二维记忆数组memo中,可以消除冗余的调用。memo[i][j]表示num[i]作为前一个元素放入/不放入LIS,并且num[j]作为当前元素放入/不放入LIS时的最长LIS。(memo[i][j] represents the length of the LIS possible using nums[i]nums[i] as the previous element considered to be included/not included in the LIS, with nums[j]nums[j] as the current element considered to be included/not included in the LIS.)。num表示所给定的数组。
ps:由暴力递归改写。递归函数可变参数 preIndex、curpos,无后效性,故memo[i][j]中缓存由特定递归过程(preIndex, curpos)计算出的值。考虑到元素的取值范围***,在上一个方法中使用的pre参数(表示LIS中的上一个元素的值)需要替换,这里使用preIndex表示该元素在nums数组中的位置。
public class Solution {
public int lengthOfLIS(int[] nums) {
// preIndex的取值范围[-1, nums.length - 1],-1表示无前缀。故长度为nums.length + 1,为了将下标为-1的值放入数组,元素整体向后移一位。
int memo[][] = new int[nums.length + 1][nums.length];
for (int[] l : memo) {
Arrays.fill(l, -1);
}
return lengthofLIS(nums, -1, 0, memo);
}
public int lengthofLIS(int[] nums, int previndex, int curpos, int[][] memo) {
// base case
if (curpos == nums.length) {
return 0;
}
// serach first
if (memo[previndex + 1][curpos] >= 0) {
return memo[previndex + 1][curpos];
}
// compute and ***
int taken = 0;
if (previndex nums[previndex]) {
taken = 1 + lengthofLIS(nums, curpos, curpos + 1, memo);
}
int nottaken = lengthofLIS(nums, previndex, curpos + 1, memo);
memo[previndex + 1][curpos] = Math.max(taken, nottaken);
return memo[previndex + 1][curpos];
}
} 复杂度分析
算法
动态规划方法依赖于这样一个事实——以i元素结尾的最长子序列不依赖于其后续元素。因此,如果已知以i元素结尾的LIS长度,可以求出以i+1元素结尾的LIS长度(通过向前搜索,尝试将i+1元素追加至其后)。
创建数组dp来存储相关数据。dp[i]表示以索引i元素作为结尾的子序列的最大长度。为了求解dp[i],尝试将当前元素(nums[i])追加到每一个既有的递增子序列中,使得添加完当前元素后的新序列仍然是递增序列,若无法添加至仍以既有子序列,则以i元素结尾的LIS长度为1。dp[i]的表达式为:
dp[i] = max(dp[j]) + 1, 0 nums[j]
即,对于dp[i],求nums[i]结尾的最长递增子序列,在nums[0..i-1]中选出比nums[i]小且长度最长(dp[j] max)的。
最后,dp[i]的最大值即为所求答案,
LIS = max(dp[i]), 0 <= i < n
ps:原著英文较差,此处意译。
public class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
dp[0] = 1;
int maxans = 1;
for (int i = 1; i < dp.length; i++) {
int maxval = 0;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
maxval = Math.max(maxval, dp[j]);
}
}
dp[i] = maxval + 1;
maxans = Math.max(maxans, dp[i]);
}
return maxans;
}
} 复杂度分析
算法
方法3花费了大量时间寻找最大dp[j]上(第二个for循环),如果dp[]是一个递增数列,那么我们可以使用二分查找进行优化,使得整个算法的复杂度下降到O(nlogn)。方法3中dp[i]保存了以nums[i]元素结尾的LIS长度,这里我们使用dp[i]保存所有长度为i+1的递增子序列中末尾元素的最小值。根据这个最小值,可以判断num数组中的后续元素是否可以追加到既有IS中以形成更长的IS。由于dp数组是递增的,所以可以使用二分查找。
举例:
nums: 2 1 5 3 6 4 8 9 7
dp[]: 2.........// 遍历至nums[0],当前长度为1的子序列末尾的最小值为2.
dp[]: 1.........// 遍历至nums[1],nums[1]
dp[]: 1 5.......// nums[2],nums[2]
dp[]: 1 3.......// dp[0]
dp[]: 1 3 6.....// num[4] > dp[2],可形成长度为3的子序列。
dp[]: 1 3 4.....// dp[1]
dp[]: 1 3 4 8...// num[6] > dp[2],形成更长的子序列。
dp[]: 1 3 4 8 9.// 形成更长的子序列
dp[]: 1 3 4 7 9.// dp[2]
最终,dp.size = 5,表示LIS=5,且所有该长度序列中最小以9结尾。
public class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
int len = 0;
for (int num : nums) {
int i = Arrays.binarySearch(dp, 0, len, num);
if (i < 0) {
i = -(i + 1);
}
dp[i] = num;
if (i == len) {
len++;
}
}
return len;
}
} 复杂度分析
参考:
https://leetcode.com/problems/longest-increasing-subsequence/solution/
classAscentSequence {public:intfindLongest(vector<int> A, intn) {if(n==0) return0;vector<int> hash(n);intans=0;for(inti=0;i<n;i++){hash[i]=1;for(intj=i-1;j>=0;j--){if(A[i]>A[j]){if(hash[j]+1>hash[i]){hash[i]=hash[j]+1;if(hash[i]>ans)ans=hash[i];}}}}returnans;}};
public class AscentSequence {
public int findLongest(int[] arr, int n) {
// 时间复杂度 O(n*lgn)
List<Integer> list = new ArrayList<>(n);
for(int i:arr){
if(list.isEmpty()||i>list.get(list.size()-1))
list.add(i);
else
list.set(getIndex(list,i,0,list.size()-1),i);
}
return list.size();
}
public int getIndex(List<Integer> list,int target,int L,int R){
while(L<R){
int mid = L+((R-L)>>1);
if(list.get(mid)<target)
L = mid+1;
else
R = mid;
}
return L;
}
}
bisect模块 import bisect class AscentSequence: def findLongest(self, A, n): res=[] for val in A: index=bisect.bisect_left(res,val) if index==len(res): res.append(val) else: res[index]=val return len(res) class AscentSequence: def findLongest(self, A, n): temp=[10**10]*n temp[0]=A[0] res=[1] for i in range(1,n): pos=self.get_index(temp,A[i]) res+=[pos+1] temp[pos]=A[i] return max(res) def get_index(self,nums,target): low,high=0,len(nums)-1 pos=0 while low<high: mid=(low+high)//2 if nums[mid]<target: low=mid+1 else: high=mid pos=high return pos
class AscentSequence {
public:
int findLongest(vector<int> A, int n) {
int B[n+1];
B[1] = A[0];
int l=1,s=0,e=l,m;
for(int i=1;i<n;i++)
{
if(A[i]>B[l])
{
l++;
B[l] = A[i]; }else{ s = 1; e = l; while(s<=e) { m = (s+e)/2; if(B[m]<A[i]) s = m+1; else e = m-1; } B[s] = A[i]; } } return l;
}
};
class AscentSequence {
public:
int findLongest(vector<int> A, int n) {
// write code here
vector<int> dp(n,0);
int res=0;
dp[0]=1;
for(int i=0;i!=n;i++){
int max=0,j=0;
while(j<i){
if(A[j]<A[i]&&dp[j]>max)
max=dp[j];
j++;
}
dp[i]=max+1;
}
for(int i=0;i!=n;i++)
if(res<dp[i])
res=dp[i];
return res;
}
};
class AscentSequence {
public:
int findLongest(vector<int> A, int n) {
if(n==1)
return 1;
vector<int> maxLen(n,1);
for(int i=1;i<n;i++)
{
for(int j=0;j<i;j++)
{
if(A[i]>A[j])
maxLen[i]=max(maxLen[i],maxLen[j]+1);
}
}
int val=maxLen[0];
for(int i=0;i<n;i++)
{
if(maxLen[i]>val)
val=maxLen[i];
}
return val;
}
};
package alex.suda.dp;
import java.util.Scanner;
public class test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int n = scanner.nextInt();
int[] A = new int[n];
for (int i = 0; i < n; i++) {
A[i] = scanner.nextInt();
}
System.out.print(findLongest(A, n));
}
}
public static int findLongest(int[] A, int n) {
// 以[2,1,4,3,1,5,6]为例。使用i来表示当前遍历的位置:
// i = 1 时,最长递增序列为{1} ,序列长度为1
// i = 2时,1 < 2 所以得丢弃2 重新建立最长递增序列{1},序列长度为1
// i = 3时,4>2,4>1 最长递增序列为{2,4} {1,4},长度为2
// 以此类推,可得:d[i+1] = max{1,d[k]+1},其中对于所有的k<= i ,A[i+1] > A[k]
// d[i] 表示A数组的前i个元素当中,最长递增序列的长度
int[] d = new int[n];
for (int i = 0; i < n; i++) {
d[i] = 1; // 初始化默认长度
for (int j = 0; j < i; j++) { // 前面最长的序列
if (A[i] > A[j] && d[j] + 1 > d[i]) { // 增加了当前的数之后,大于原来的长度,就更新
d[i] = d[j] + 1;
}
}
}
int maxDis = Integer.MIN_VALUE;
for(int i=0;i<n;i++){
if(d[i] > maxDis){
maxDis = d[i];
}
}
return maxDis;
}
}
import java.util.*;
public class AscentSequence {
public int findLongest(int[] A, int n) {
// write code here
int[] dp = new int[n];
int max = Integer.MIN_VALUE;
for (int i = 0; i < n; ++i) {
if (i == 0) {
dp[i] = 1;
} else {
dp[i] = 1;
for (int j = i - 1; j >= 0; --j) {
if (A[j] < A[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
max = Math.max(dp[i], max);
}
return max;
}
}
//C++ O(n^2)解
//时间复杂度为0(n^2)
class AscentSequence {
public:
int findLongest(vector<int> A, int n) {
// write code here
vector<int>vi(n);
for(int i = 0; i < n; i++)
vi[i] = 1;
for(int i = 1;i < n; i++)
for(int j = 0;j < i;j++){
if(A[i] > A[j]){
vi[i] =vi[i]>(vi[j]+1)?vi[i]:vi[j]+1;
}
}
sort(vi.begin(),vi.end());
return vi[n-1];
}
};