对于一个数字序列,请设计一个复杂度为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]; } };