【LeetCode】300.最长递增子序列——暴力递归(O(n^3)),动态规划(O(n^2)),动态规划+二分法(O(nlogn))

  算法新手,刷力扣遇到这题,搞了半天终于搞懂了,来这记录一下,欢迎大家交流指点。

 

题目描述:

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

 

解法一:暴力递归

  不解释,先暴力搞一下。(时间复杂度O(n^3),不行)

 1 class Solution {
 2 public:
 3     int l(vector<int>& nums) { // 返回以nums[0]开头的最长递增序列长度
 4         if (nums.size() < 2)
 5             return nums.size();
 6         int max_len = 1;
 7         for (int i = 1; i < nums.size(); ++ i)
 8             if (nums[i] > nums[0]) {
 9                 vector<int> t{nums.begin() + i, nums.end()};
10                 max_len = max(max_len, l(t) + 1);
11             }
12         return max_len;
13     }
14     int lengthOfLIS(vector<int>& nums) { // 所有序列遍历一遍
15         int max_len = 1;
16         for (i = 0; i < nums.size(); ++ i) {
17             vector<int> t(nums.begin() + i, nums.end());
18             max_len = max(max_len, l(t));
19         }
20         return max_len;
21     }
22 };

   小优化一下,记忆化搜索。(还是不行,时间复杂度还是太高)

 1 class Solution {
 2 public:
 3     int l(unordered_map<int, int>& map, vector<int>& nums) {
 4         if (nums.size() < 2)
 5             return nums.size();
 6         if (map.find(nums[0]) != map.end()) // 如果已经知道了以某个数开头的元素的最长序列数,直接返回
 7             return map[nums[0]];
 8         int max_len = 1;
 9         for (int i = 1; i < nums.size(); ++ i)
10             if (nums[i] > nums[0]) {
11                 vector<int> t{nums.begin() + i, nums.end()};
12                 max_len = max(max_len, l(map, t) + 1);
13             }
14         map[nums[0]] = max_len; // 记录以某个数开头的最长递增序列长度
15         return max_len;
16     }
17     int lengthOfLIS(vector<int>& nums) {
18         int max_len = 1;
19         unordered_map<int, int> map; // 哈希表,<开头的数,最长递归序列长度>
20         for (int i = 0; i < nums.size(); ++ i) {
21             vector<int> t(nums.begin() + i, nums.end());
22             max_len = max(max_len, l(map, t));
23         }
24         return max_len;
25     }
26 };

 

解法二:动态规划

  看来暴力是不行滴,还得动态规划。(时间复杂度O(n^2),AC了)

 1 class Solution {
 2 public:
 3     int lengthOfLIS(vector<int>& nums) {
 4         vector<int> dp(nums.size(), 0); // 记录以nums[i]为结尾的最长递增子序列长度
 5         for (int i = 1; i < nums.size(); ++ i)
 6             for (int j = 0; j < i; ++ j) // 找一个最长的递增序列,接到它后面
 7                 if (nums[j] < nums[i])
 8                     dp[i] = max(dp[i], dp[j] + 1);
 9         return *max_element(dp.begin(), dp.end()) + 1;
10     }
11 };

 

解法三:动态规划 + 二分查找

  动态规划方法是可行的,但是O(n^2)的时间复杂度还是较高,使用二分查找方法可以进一步优化。(时间复杂度O(nlogn),大提升)

 1 class Solution {
 2 public:
 3     int lengthOfLIS(vector<int>& nums) {
 4         vector<int> dp(1, *nums.begin()); // 维护一个数组,用来存放最长的递增子序列
 5         int left = 0, right = 0, mid = 0;
 6         for (int i = 1; i < nums.size(); ++ i) { // 遍历一遍nums寻找每个元素在最长子序列中的插入位置
 7             if (nums[i] > *(dp.end() - 1)) { // 如果当前元素比序列中所有元素都大,直接插到末尾
 8                 dp.push_back(nums[i]);
 9                 continue;
10             }
11             left = -1;              // 否则的话,替换掉序列中第一个大于等于它的元素,这样可以保证得到最长的递增序列
12             right = dp.size();
13             while (left + 1 != right) {
14                 mid = (left + right) / 2;
15                 if (dp[mid] >= nums[i])
16                     right = mid;
17                 else
18                     left = mid;
19             }
20             dp[right] = nums[i];
21         }
22         return dp.size();
23     }
24 };

 

上一篇:数据结构与算法 8.归并排序 mergeSort


下一篇:3.31面试pony.ai