无序
整数配列が与えられた場合、最長の昇順部分列を見つけます长度
。
注:ここでは、サブシーケンスは連続している必要はありません。
例:
输入:[10, 9, 2, 5, 3, 7, 101, 18]
输出:4
解释:最长的上升子序列是[2, 3, 7, 101],长度为4
説明:
- 最長の昇順サブシーケンスには複数の組み合わせがある場合があり、対応する長さを出力するだけで済みます。
- アルゴリズムの時間計算量はO(n ^ 2)である必要があります
上級:
- アルゴリズムの時間計算量をO(nlgn)に減らすことができますか?
方法1:動的計画法
アイデア:
意味を定義するにはdp最初の配列:サブインクリメントのシーケンスの最長の長さの数dp[i]
として表されます。nums[i]
结尾
といった:
インデックス | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
nums | 1 | 4 | 3 | 4 | 2 | 3 |
dp | 1 | 2 | 2 | 3 | 2 | ? |
アルゴリズムの進化プロセスは次のとおりです。
この定義によれば、最終結果(サブシーケンスの最大長)は、dp配列の最大値、つまり次のようになります。
int res = 0;
for(int i = 0; i < dp.length; i++){
res = Math.max(dp[i], res);
}
return res;
状態遷移方程式を見つけます。
以前のdp配列の定義によると、今はdp[5]
値を見つけたい、つまりnums[5] 为结尾
、最長増加部分列を見つけたいと思っています。
nums[5] = 3
これは増加するサブシーケンスであるため、末尾が3未満の前のサブシーケンスを見つけてから、末尾に3を追加して新しい増加するサブシーケンスを形成するだけで、この新しいサブシーケンスの長さが1つ長くなります。
もちろん、多くの新しいサブシーケンスが形成される可能性がありますが、必要なのは最長のものだけであり、最長のサブシーケンスの長さをdp [5]の値として使用できます。
dp [5] = max {dp [0] + 1、nums [0] = 1 <nums [5] dp [4] + 1、nums [4] = 2 <nums [5] dp [5] = max \ begin {cases} dp [0] + 1、\ quad nums [0] = 1 <nums [5] \\ dp [4] + 1、\ quad nums [4] = 2 <nums [5] \ end {cases } d p [ 5 ]=m a x{{ d p [ 0 ]+1 、n u m s [ 0 ]=1<n u m s [ 5 ]d p [ 4 ]+1 、n u m s [ 4 ]=2<N U M S [ 5 ]
最終的な状態遷移方程式は次のとおりです。
dp [n] = max {dp [i] + 1、i∈[0、n − 1] andnums [i] <nums [n]} dp [n] = max \ {dp [i] + 1、\ quad i \ in [0、n-1] \ quadおよび\ quad nums [i] <nums [n] \} d p [ n ]=m a x { d p [ i ]+1 、私∈[ 0 、n−1 ]a n dn u m s [ i ]<n u m s [ n ] }
基本ケースは1であり、サブシーケンスには少なくともそれ自体が含まれている必要があるため、dp配列はすべて1に初期化する必要があります。したがって、最小の長さは1です。
import java.util.*;
class Solution {
public int findNumberOfLIS(int[] nums) {
int[] dp = new int[nums.length];
Arrays.fill(dp, 1);
int res = 0;
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)
方法2:二分探索のアイデア
二分探索のアイデアについては、この記事を見ることができます動的計画法設計の最長増加部分列
class Solution {
public int findNumberOfLIS(int[] nums) {
int[] top = new int[nums.length];
int piles = 0;
for(int i = 0; i < nums.length; i++){
int poker = nums[i];
int left = 0;
int right = piles;
while(left < right){
int mid = left + (right - left)/2;
if(top[mid] > poker){
right = mid;
}else if(top[mid] < poker){
left = mid + 1;
}else{
right = mid;
}
}
if(left == piles) piles++;
top[left] = poker;
}
return piles;
}
}
時間計算量:O(nlgn)
スペースの複雑さ:O(n)