LeetCode-動態規劃總結(二)
最長遞增子序列
已知一個序列 {S1, S2,…,Sn},取出若干陣列成新的序列 {Si1, Si2,…, Sim},其中 i1、i2 … im 保持遞增,即新序列中各個數仍然保持原數列中的先後順序,稱新序列為原序列的一個 子序列 。
如果在子序列中,當下標 ix > iy 時,Six > Siy,稱子序列為原序列的一個 遞增子序列 。
定義一個數組 dp 儲存最長遞增子序列的長度,dp[n] 表示以 Sn 結尾的序列的最長遞增子序列長度。對於一個遞增子序列 {Si1, Si2,…,Sim},如果 im < n 並且 Sim < Sn,此時 {Si1
因為在求 dp[n] 時可能無法找到一個滿足條件的遞增子序列,此時 {Sn} 就構成了遞增子序列,需要對前面的求解方程做修改,令 dp[n] 最小為 1,即:
對於一個長度為 N 的序列,最長遞增子序列並不一定會以 SN 為結尾,因此 dp[N] 不是序列的最長遞增子序列的長度,需要遍歷 dp 陣列找出最大值才是所要的結果,max{ dp[i] | 1 <= i <= N} 即為所求。
最長遞增子序列
300. Longest Increasing Subsequence (Medium)
class Solution {
public int lengthOfLIS(int[] nums) {
if (null == nums || 0 == nums.length) {
return 0;
}
int n = nums.length;
int[] dp = new int[n];
Arrays.fill(dp, 1);
int max = Integer. MIN_VALUE;
for (int i = 0; i < n; i++) {
for (int j = 0; j <= i - 1; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
max = Math.max(max, dp[i]);
}
return max == Integer.MIN_VALUE ? 1 : max;
}
}
以上解法的時間複雜度為 O(N2),可以使用二分查詢將時間複雜度降低為 O(NlogN)。
定義一個 tails 陣列,其中 tails[i] 儲存長度為 i + 1 的最長遞增子序列的最後一個元素。對於一個元素 x,
- 如果它大於 tails 陣列所有的值,那麼把它新增到 tails 後面,表示最長遞增子序列長度加 1;
- 如果 tails[i-1] < x <= tails[i],那麼更新 tails[i] = x。
例如對於陣列 [4,3,6,5],有:
tails len num
[] 0 4
[4] 1 3
[3] 1 6
[3,6] 2 5
[3,5] 2 null
可以看出 tails 陣列保持有序,因此在查詢 Si 位於 tails 陣列的位置時就可以使用二分查詢。
class Solution {
public int lengthOfLIS(int[] nums) {
if (null == nums || 0 == nums.length) {
return 0;
}
int[] tail = new int[nums.length];
int len = 0;
for (int num : nums) {
int index = binarySearch(tail, len, num);
tail[index] = num;
if (index == len) {
len++;
}
}
return len;
}
public int binarySearch(int[] tail, int len, int target) {
int left = 0, right = len;
while (left < right) {
int mid = left + (right - left) / 2;
if (tail[mid] == target) {
return mid;
} else if (tail[mid] > target) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
一組整數對能夠構成的最長鏈
646. Maximum Length of Pair Chain (Medium)
Input: [[1,2], [2,3], [3,4]]
Output: 2
Explanation: The longest chain is [1,2] -> [3,4]
題目描述:對於 (a, b) 和 (c, d) ,如果 b < c,則它們可以構成一條鏈。
class Solution {
public int findLongestChain(int[][] pairs) {
if (null == pairs || 0 == pairs.length) {
return 0;
}
int m = pairs.length;
int n = pairs[0].length;
int[] dp = new int[m];
Arrays.fill(dp, 1);
int max = Integer.MIN_VALUE;
Arrays.sort(pairs, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
for (int i = 0; i < m; i++) {
int c = pairs[i][0];
for (int j = 0; j <= i - 1; j++) {
int b = pairs[j][1];
if (c > b) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
max = Math.max(max, dp[i]);
}
return max == Integer.MIN_VALUE ? 1 : max;
}
}
最長擺動子序列
376. Wiggle Subsequence (Medium)
Input: [1,7,4,9,2,5]
Output: 6
The entire sequence is a wiggle sequence.
Input: [1,17,5,10,13,15,10,5,16,8]
Output: 7
There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8].
Input: [1,2,3,4,5,6,7,8,9]
Output: 2
要求:使用 O(N) 時間複雜度求解。
class Solution {
public int wiggleMaxLength(int[] nums) {
if (null == nums || 0 == nums.length) {
return 0;
}
int n = nums.length;
int up = 1, down = 1;
for (int i = 1; i < n; i++) {
if (nums[i] < nums[i - 1]) {
down = up + 1;
} else if (nums[i] > nums[i - 1]){
up = down + 1;
}
}
return Math.max(up, down);
}
}