From 53570dab62a1597e251e57c0c33091096804f133 Mon Sep 17 00:00:00 2001 From: ShusenTang Date: Fri, 8 Nov 2019 23:59:22 +0800 Subject: [PATCH] Create 300. Longest Increasing Subsequence.md --- .../300. Longest Increasing Subsequence.md | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 solutions/300. Longest Increasing Subsequence.md diff --git a/solutions/300. Longest Increasing Subsequence.md b/solutions/300. Longest Increasing Subsequence.md new file mode 100644 index 0000000..c5fe656 --- /dev/null +++ b/solutions/300. Longest Increasing Subsequence.md @@ -0,0 +1,113 @@ +# [300. Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/) +# 思路 +给定一个数组,求最长递增子序列的长度。 + +## 思路一、O(n^2) +很明显是一个动态规划题,我们可以用dp[i]表示以nums[i]为结尾的最长子序列的长度。这样更新dp[i]的转移方程就为 +``` +for all j in [0, i-1]: + if nums[i] >= nums[j]: + dp[i] = max(dp[i], dp[j] + 1) +``` +从转移方程可以看出有两层循环,所以时间复杂度为O(n^2)。 + +## 思路二、O(nlogn) +思路一比较好想,O(nlogn)的就不太好想了,可参考[这里](https://segmentfault.com/a/1190000003819886),讲得比较清楚。 + +核心思路就是: +> 维护一个长度可变的数组tails, tails[i]表示长度为i+1的所有递增子序列中最小的那个末尾元素,这样更新完毕后tails的长度即结果。 + +下面举个[例子](https://segmentfault.com/a/1190000003819886)说明: +在`1,3,5,2,8,4,6`这个例子中,当到6时,我们一共可以有四种长度的递增子序列,在每种子序列中我们取末尾元素最小的,那么这四个序列分别是 +``` +1 +1,2 +1,3,4 +1,3,5,6 +``` +为什么要选末尾元素最小的呢,是因为在长度一定的情况下,末尾元素最小的是未来最有可能成为最长序列的候选人。这样,每来一个新的数, +我们便按照以下规则更新这些序列: +1. 如果nums[i]比所有序列的末尾都大,或等于最大末尾,说明有更长的递增序列产生,我们把最长的序列复制一遍,并加上这个nums[i]。 +2. 如果nums[i]比所有序列的末尾都小,说明长度为1的序列可以更新了,更新为这个更小的末尾。 +3. 如果在中间,则更新那个末尾数字刚刚大于等于自己的那个序列,说明那个长度的序列可以更新了。 + +比如这时,如果再来一个9,那就是第1种情况,更新序列为 +``` +1 +1,2 +1,3,4 +1,3,5,6 +1,3,5,6,9 +``` + +如果再来一个3,那就是第3种情况,更新序列为 +``` +1 +1,2 +1,3,3 +1,3,5,6 +``` +如果再来一个0,那就是第2种情况,更新序列为 +``` +0 +1,2 +1,3,3 +1,3,5,6 +``` + +前两种都很好处理,O(1)就能解决,主要是第三种情况,实际上我们观察直到6之前这四个不同长度的升序序列,他们末尾是递增的, +所以可以用二分搜索来找到适合的更新位置。 + + +# C++ +## 思路一 +``` C++ +class Solution { +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + if(n <= 1) return n; + vectordp(n, 1); + + int res = 1; + for(int i = 1; i < n; i++){ + // j >= 0 可优化成 j >= dp[i]-1 + for(int j = i-1; j >= 0; j--) + if(nums[i] > nums[j]) + dp[i] = max(dp[i], dp[j] + 1); + + res = max(res, dp[i]); + } + return res; + } +}; +``` + +## 思路二 +``` C++ +class Solution { +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + if(n <= 1) return n; + + vectortails; + tails.push_back(nums[0]); + for(int i = 1; i < n; i++){ + if(nums[i] > tails.back()) tails.push_back(nums[i]); + else{ + tails[lower_bound(tails.begin(), tails.end(), nums[i]) - tails.begin()] = nums[i]; + // 上面这句等价于下面注释部分 + // int low = 0, high = tails.size() - 1; + // while(low < high){ + // int mid = low + (high - low) / 2; + // if(tails[mid] < nums[i]) low = mid + 1; + // else high = mid; + // } + // tails[low] = nums[i]; + } + } + return tails.size(); + } +}; +```