给定一个数字序列,求其最长上升子序列java
测试用例 int[] nums = {4,2,4,5,3,7}; 预期结果 4, 序列是{2,4,5,7}
public int lengthOfLIS(int[] nums){ }
时间复杂度为
O(N^2)
数组
public int longestIncreasingSubsequence(int[] nums) { if(nums == null || nums.length == 0){ return 0; } //dp[i] : 以nums[i]结尾的LIS的长度 int n = nums.length; int[] dp = new int[n] ; //初始化 Arrays.fill(dp, 1); for(int i = 0; i < n; i++){ //nums[i]以前的每个数字 for(int j = 0; j < i; j++){ if(nums[i] > nums[j]){ dp[i] = Math.max(dp[i], dp[j] + 1); } } } //dp中的最大值 int res = dp[0]; for (int i = 1; i < n; i++) { res = Math.max(res, dp[i]); } return res; }
时间复杂度为
O(N*logN)
,单独去理解比较难,在动态规划的解法上进一步思考会更好理解函数
数组{4, 2, 4, 5, 2,7}, 用DP解法能够获得,dp = {1, 1, 2, 3, 2,7}。假设计算dp[3], DP解法在获得dp[3]时,须要顺序查找3以前的dp值中可用的(序列尾元素小于nums[3]的)最大值,其实可使用二分查找。但想使用二分查找前,先要证实一些结论。测试
经过dp数组咱们知道dp[3]前面的dp有:设计
{4}、{2}
,它们中最小的尾元素是2{*, 4}
, 它们中最小的尾元素是4结论1 : 数值相等的那些dp只须要查找最小的尾元素是否可用code
结论2 : dp值递增时,它们中最小的尾元素确定也是递增的class
有了这两个结论,能够用tail[j]记录dp值为j的序列们的最小尾元素,dp[i]的值能够经过对tail进行二分查找可用的尾元素值来获得动态规划
public int longestIncreasingSubsequence(int[] nums) { int n = nums.length; //minTail[i] 全部长度为i的子序列中最小的尾元素的值 int[] minTail = new int[n + 1]; minTail[0] = Integer.MIN_VALUE; //最后一个记录的位置 int maxLen = 0; for (int i = 0; i < n; i++) { int prePlace = 0; int start = 0, end = maxLen, mid; while (start <= end) { mid = start + (end - start) / 2; //nums[i]会放到最后一个比它小的堆的右边 if (nums[i] > minTail[mid]) { prePlace = mid; start = mid + 1; } else { end = mid - 1; } } //更新nums[i]放的堆的最小值 minTail[prePlace + 1] = nums[i]; //全部的堆的最小值都比它小,另起一堆 if (prePlace + 1 > maxLen) { maxLen = prePlace + 1; } } return maxLen; }