Given an unsorted array of integers, find the length of longest increasing subsequence.java
For example,
Given [10, 9, 2, 5, 3, 7, 101, 18]
,
The longest increasing subsequence is [2, 3, 7, 101]
, therefore the length is 4
. Note that 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.less
Follow up: Could you improve it to O(n log n) time complexity?優化
問題分析:this
根據問題抽象出最優子結構。設定原數組爲S[n],以元素S[k]爲結尾的最長遞增子序列長度爲list[k].則list[n + 1] = max(list[k] + 1), 其中k = 0, 1, ..., n, 且s[n + 1] > s[k].即在尋找以S[n + 1]爲結尾的最長遞增子序列長度時,都須要找出原數組中在S[n + 1]前面小於S[n + 1]的元素。spa
利用簡單的dp作法時間複雜度爲O(n^2),空間複雜度爲O(n).代碼以下:code
public class Solution { public int lengthOfLIS(int[] nums) { if (nums == null || nums.length == 0) return 0; int[] rst = new int[nums.length]; for (int i = 0; i < nums.length; i++) { rst[i] = 1; } int result = 1; for (int i = 1; i < nums.length; i++) { for (int j = 0; j < i; j++) { if (nums[j] < nums[i] && rst[j] + 1 > rst[i]) { rst[i] = rst[j] + 1; } } result = Math.max(result, rst[i]); } return result; } }
問題優化:blog
一樣採用動態規劃的思想,能夠使用一個數組dp[]用來記錄以該下標加1爲LIS個數的最後一個元素的最小值。能夠發現使用數組dp[0...MAXLENGTH - 1]其中dp[i]表示長度爲i + 1的LIS的最小末尾元素。對於dp[0...MAXLENGTH - 1]中值合法對應的最大下標加1,就是當前最長的LIS,也即利用dp[]更新自身。這種狀況下,對於每次新添加的元素,只須要在dp[]數組中尋找該元素應該插入的位置便可。這一步能夠利用Arrays.binarySearch來完成,從而將總體的時間複雜度由O(n^2)降低到O(n log n) 。ip
從下面Arrays.binarySearch的javadoc能夠看到:ci
1)找到key元素,返回key元素下標;
2)沒找到key元素,返回(-(insertion point) - 1).其中insertion point爲第一個比key元素大的元素的index,若是都比key小,則爲toIndex。
public static int binarySearch(int[] a, int fromIndex, int toIndex, int key) Searches a range of the specified array of ints for the specified value using the binary search algorithm. The range must be sorted (as by the sort(int[], int, int) method) prior to making this call. If it is not sorted, the results are undefined. If the range contains multiple elements with the specified value, there is no guarantee which one will be found. Parameters: a - the array to be searched fromIndex - the index of the first element (inclusive) to be searched toIndex - the index of the last element (exclusive) to be searched key - the value to be searched for Returns: index of the search key, if it is contained in the array within the specified range; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the array: the index of the first element in the range greater than the key, or toIndex if all elements in the range are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found. Throws: IllegalArgumentException - if fromIndex > toIndex ArrayIndexOutOfBoundsException - if fromIndex < 0 or toIndex > a.length Since: 1.6
利用這兩點,當該數組元素沒找到key元素時,將insertion point處的元素替換爲key元素,且若是key元素是在末尾新加時,將結果加1.
代碼以下:
public class Solution { public int lengthOfLIS(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int[] dp = new int[nums.length]; int len = 0; for (int i = 0; i < nums.length; i++) { int index = Arrays.binarySearch(dp, 0, len, nums[i]); if (index < 0) { index = - (index + 1); } dp[index] = nums[i]; if (index == len) { len++; } } return len; } }
值得注意的是,這個保存遞增數列的數組最終存的長度爲len的遞增數列不必定就是實際的結果序列,雖然其長度與結果序列相同。
好比nums爲[2,3,4,1],最後len爲3,dp中的數列爲[1,3,4]。
這個題目的解法確實很巧妙,有一些小細節值得仔細思考。