题目描述
标准解法一:DP $O(n^2)$
$d[i] = max(d[j])+1, 0 ≤ j < i \&\& nums[j] > nums[i]$
即$d[i]$表示以$nums[i]$结尾的最长递增子序列长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Solution { public: int lengthOfLIS(vector<int>& nums) { int n=(int)nums.size(); if (n == 0) return 0; vector<int> dp(n, 0); for (int i = 0; i < n; ++i) { dp[i] = 1; for (int j = 0; j < i; ++j) { if (nums[j] < nums[i]) { dp[i] = max(dp[i], dp[j] + 1); } } } return *max_element(dp.begin(), dp.end()); } };
|
标准解法二:贪心+二分 $O(nlogn)$
思路:记录长度为i
的LIS的最小尾元素d[i]
贪心贪什么?
贪尾元素的最小,从而保证每个长度为i
的递增子序列(IS,Increasing Sequence)都是最容易扩充的。
二分分什么?
分最小尾元数组d
,当num[i]
大于当前最大长度len
对应的尾元d[len]
时,秩序将num[i]
“插入”LIS(直接令d[++len]=num[i]
即可完成更新d
与len
);而如果无法插入当前的LIS,则需要考虑更新之前的IS,因为每个IS都有可能成为LIS。所以需要找到d[k] < num[i] < d[k+1]
,也就是找到第一个比num[i]
小的尾元d[k]
,这里就用到了二分。然后把num[i]
“插到”d[k]
之后(更新d[k+1]=num[i]
)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class Solution { public: int lengthOfLIS(vector<int>& nums) { int len = 1, n = (int)nums.size(); if (n == 0) return 0; vector<int> d(n + 1, 0); d[len] = nums[0]; for (int i = 1; i < n; ++i) { if (nums[i] > d[len]) d[++len] = nums[i]; else{ int l = 1, r = len, pos = 0; while (l <= r) { int mid = (l + r) >> 1; if (d[mid] < nums[i]) { pos = mid; l = mid + 1; } else r = mid - 1; } d[pos + 1] = nums[i]; } } return len; } };
|
个人解法 DP
思路类似标准解法一
,用map P
记录以nums[i]
结尾的的最大递增子序列(LIS),每次取最大的LIS长度P[nums[j]]
使得nums[i]>nums[j]
保留
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Solution { public: int lengthOfLIS(vector<int>& nums) { map<int, int> P;
for (int i = 0; i < nums.size(); i++) { P[nums[i]] = 1; }
for (int i = 0; i < nums.size(); i++) { int j; for (j = 0; j < i; j++) { if (nums[i] > nums[j] && P[nums[i]] < P[nums[j]]+1) { P[nums[i]] = P[nums[j]]+1; } } }
int max = 0; for (auto it = P.begin(); it != P.end(); it++) { if ((*it).second > max) { max = (*it).second; } } return max; } };
|
总结一下,DP解这题的思路只需由i
建立nums[i]
与dp数组dp[i]
的联系,而无需用到map
进行具体数值的映射。此外,对max
的处理不够简略,比较混乱
参考
官方题解