給定一個數字序列,求其最長上升子序列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; }