LeetCode 300. Longest Increasing Subsequence —— 最長上升子序列(Java)
Given an unsorted array of integers, find the length of longest increasing subsequence.
Example:
Input: [10,9,2,5,3,7,101,18] Output: 4 Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
Note:
- 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.
Follow up: Could you improve it to O(n log n) time complexity?
解法一,O(n2):
直觀想一下,選中一個元素,以這個元素為結尾的子序列前面有多少個比他值小的元素,那麽以它為結尾的上升子序列就是多少再加一。即當前狀態可以由之前的一個或一些狀態推導出來,所以可以用動態規劃。建立一個一維數組dp,dp[i]表示以nums[i]結尾的最長上升子序列長度。初始都置為1。對於原數組每個元素,二重循環從頭遍歷原數組,每當找到一個比當前元素小的值,證明至少可以形成一個dp[j]+1的上升子序列,所以dp[i] = max(dp[i], dp[j] + 1),而dp[j]之前已經求得。
解法二,O(n log n):
還是想一下“人工”做這個題是什麽過程。按照上面的例子來分析:
首先看到10,加入備選集,備選集合為{10};
之後看到了9,沒有形成上升序列,那麽9不應該加入備選集合。但是因為9小於10,所以如果把10替換成9會增加接下來產生上升序列的機會,且並不影響備選集合元素的個數(因為是替換),所以替換掉,備選集現在有{9};
遇到2道理同上,替換掉9,備選集變成{2};
遇到5,這時候形成了上升序列,此時應該是添加到備選集合,變為{2,5};
遇到3,沒有形成上升序列,但還是道理同加入9的情況,如果此時把5替換成3,會增加接下來形成上升序列的機會,且備選集保持上升,並且個數也沒變,所以替換掉5,備選集變成{2,3};
遇到7,同遇到5,添加元素,備選集{2,3,7};
遇到101,同上,備選集{2,3,7,101};
遇到18,還是一樣,雖然沒有形成上升序列,但是如果把101替換掉,那麽接下來形成上升序列的機會會增加,並且備選集的上升屬性和元素個數都不變,所以替換,備選集變為{2,3,7,18}。
至此所有元素添加完畢,備選集的元素個數就是最長上升子序列長度。但這裏註意,備選集裏面的元素並不是最後最長子序列的元素。因為在尋找最長子序列的過程中,目標是盡可能的讓以後形成上升序列的機會增加,所以進行了替換。
“人工”做出來之後,只要用程序實現思考過程就好。總結起來就是:
如果遇到的元素比備選集合裏面的元素都大,那麽就添加進去,使得上升序列長度增加;
如果遇到的元素比備選集合裏最後一個元素小,那麽代表它無法被添加到備選集。但是為了使後面得到上升序列的機會增加,需要在不破壞集合上升屬性和元素總數的情況下,替換掉備選集中的元素,那麽就是替換掉大於他的元素中最小的那個,這樣才能滿足條件。
這時候,發現備選集一直是保持有序,尋找替換元素的時候就可以用到二分查找,得到O(n log n)的時間復雜度。其中還要註意的是如果元素已經在備選集合中,是不需要任何操作的,因為它並不能增加上升序列的長度,也不會增加之後遇到上升序列的機會,所以直接跳過。
Java(解法一)
class Solution { public int lengthOfLIS(int[] nums) { if (nums == null || nums.length == 0) return 0; int[] dp = new int[nums.length]; int max = 1; for (int i = 0; i < nums.length; i++) dp[i] = 1; for (int i = 0; i < nums.length; i++) { for (int j = 0; j <= i; j++) { if (nums[j] < nums[i]) { dp[i] = Math.max(dp[i], 1 + dp[j]); max = max > dp[i] ? max : dp[i]; } } } return max; }
Java(解法二)
class Solution { public int lengthOfLIS(int[] nums) { if (nums == null || nums.length == 0) return 0; List<Integer> dp = new ArrayList<>(); dp.add(nums[0]); for (int i = 1; i < nums.length; i++) { if (dp.contains(nums[i])) continue; else if (nums[i] > dp.get(dp.size()-1)) dp.add(nums[i]); else if (nums[i] < dp.get(dp.size()-1)) { int l = 0, r = dp.size()-1; while (l < r) { int mid = l + (r - l) / 2; if (dp.get(mid) < nums[i]) l = mid + 1; else r = mid; } dp.set(r, nums[i]); } } return dp.size(); } }
LeetCode 300. Longest Increasing Subsequence —— 最長上升子序列(Java)