LeetCode第300題題目地址。
題目描述:
給定一個無序的整數數組,找到其中最長上升子序列的長度。
例子:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
解法一:動態規劃
動態規劃的核心設計思想是數學歸納法。從第一個元素開始找,你思考的過程,將其歸納總結,轉化爲用代碼描述你思考的過程。
馬士兵老師說過,代碼不是一口氣寫出來的,一點點拆解,最後再組合,再去調試,處理邊界,最終將問題解決。這裏我們拆開解決解決。
1.狀態定義:dp[i]
表示以 nums[i]
這個數結尾的最長遞增子序列的長度。狀態的初始值,根據這個定義,我們就可以推出 base case:dp[i]
初始值爲 1,因爲以 nums[i]
結尾的最長遞增子序列起碼要包含它自己。
最終的返回結果爲res,那麼遍歷數組,找出最大的dp[i],代碼如下:
int res = 0;
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
for (int i = 0; i < nums.length; i++) {
res = Math.max(res, dp[i]);
}
接下來看dp[i]應該怎麼計算,也就是狀態方程該如何確定吶?看下面的例子,根據剛纔我們對 dp
數組的定義,現在想求 dp[5]
的值,也就是想求以 nums[5]
爲結尾的最長遞增子序列。
index | 0 | 1 | 2 | 3 | 4 | 5 |
nums | 10 | 9 | 2 | 5 | 3 | 7 |
dp | 1 | 1 | 1 | 2 | 2 | ? |
nums[5] = 7
,既然是遞增子序列,我們只要找到前面那些結尾比 7 小的子序列,然後把 7接到最後,就可以形成一個新的遞增子序列,而且這個新的子序列長度加一。可能形成很多種新的子序列,但是我們只選擇最長的那一個,把最長子序列的長度作爲 dp[5]
的值即可。代碼描述:
//求第i個元素的最長子序列
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
到這裏,核心部分就寫完了,來看下完整代碼:
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) return 0;
int res = 0;
int[] dp = new int[nums.length];
Arrays.fill(dp, 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], dp[j] + 1);
}
res = Math.max(res, dp[i]);
}
return res;
}
時間複雜度O(n^2),空間複雜度O(n)
解法二:二分查找
解析:參考的jyd大神的解題方法,短而精湛。
1.狀態定義:tails[k] 的值代表長度爲k+1子序列 的尾部元素值。
2.設 res 爲 tails當前長度,代表直到當前的最長上升子序列長度。設 j∈[0,res),考慮每輪遍歷 nums[k] 時,通過二分法遍歷 [0,res)列表區間,找出 nums[k]的大小分界點,會出現兩種情況:
- 區間中存在 tails[i] > nums[k]: 將第一個滿足 tails[i] > nums[k]執行 tails[i] = nums[k] ;因爲更小的 nums[k]後更可能接一個比它大的數字(前面分析過)。
- 區間中不存在 tails[i] > nums[k]: 意味着 nums[k]可以接在前面所有長度的子序列之後,因此肯定是接到最長的後面(長度爲 res),新子序列長度爲res + 1。
3.初始狀態:令tails列表所有值=0。
4.返回值:返回res,即最長上升子子序列長度。
class Solution {
public int lengthOfLIS(int[] nums) {
int[] tails = new int[nums.length];
int res = 0;
for(int num : nums) {
int i = 0, j = res;
while(i < j) {
int m = (i + j) / 2;
if(tails[m] < num) i = m + 1;
else j = m;
}
tails[i] = num;
if(res == j) res++;
}
return res;
}
}
時間複雜度O(nlogn),空間複雜度O(n)