Leetcode:300.最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。

示例:

输入: [10,9,2,5,3,7,101,18]
输出: 4 
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4

说明:

  • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
  • 你算法的时间复杂度应该为 O(n2) 。

进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

解题思路:

1. 普通算法

动态规划。假设数组的前n个数的所有上升子序列是vector<vector<int>> data。第n+1个数将会对data产生影响。1. 如果data[i-1].back()<nums[n+1],那么将nums[n+1]加入数组末尾。2.由于最终的结果未必包含nums[n+1],于是当加入nums[n+1]时需要将data[i-1]复制一份加入data。这样表面上解决了问题,但是实际上如果每次都需要将nums[n+1]加入data中所有序列的后端(nums升序),那么data的规模将指数上升,明显是不可取的。

后来一想,将nums[n+1]加入data的所有可能序列的末端,这个操作有很大的改进空间,因为nums[n+1]出现之后,以nums[n+1]结尾的最长升序是确定的而且是唯一的,取决于data中升序列末端小于nums[n+1]的最长一个,按照这个想法就可以很好地解决上述的内存不足的现象。

很显然data这个结构已经不适用了,改用可以自动排序的哈希容器map<int,int>mp。其中容器的key代表数字nums,val代表当前以数字nums结尾出现的最长升序列。当遇到nums[n+1]时,只需访问key比nums[n+1]小的pair,在这些key中找一个val最大的,然后当前nums[n+1]结尾的最大升序长度即是val+1。如果没有比nums[n+1]跟小的key,令nums[n+1]=1。如此,mp.size()<=nums.size(),取决于nums中不重复元素的个数,空间复杂度O(n)。时间复杂度O(n)-O(n^2)之间。已经满足了题目要求。

事实上,这个算法测试时间24ms,击败48%,仍有改进的空间。

2. 高级算法

动态规划,二分查找。假设vector<int> data是前n个数查找得到的最长升序列。当nums[n+1]出现时,可能有以下三种情况:

  1. nums[n+1]是data中的一个数,那么nums[n+1]不会影响原有的升序列。
  2. nums[n+1]恰好小于data[i],那么data[i]=nums[n+1]。原有的升序列长度不变,只需要理解为什么更新data[i]的值。我们假设最终结果里n+1之后升序列是n_val,如果满足(data[i],n_val)是升序,那么(nums[n+1],n_val)也必然是升序,这个时候讲nums[n+1]替换data[i]这一步是否存在不重要,可有可无,因为求解的是长度。如果不满足(data[i],n_val)是升序,那么,必须替将data[i]换nums[n+1],(nums[n+1],n_val)才是升序。综上,应该替换,但是由此可知,这这么一来,想要求解升序列是什么就难了。
  3. 剩下只可能是nums[n+1]大于data中的最后一个数,在末尾追加即可。
  4. 以上的查找,都用二分查找,特殊之处在于返回1,2,3三种情况。

这样一来时间复杂度就成了O(n)-O(nlogn),空间复杂度为O(n)。

普通解法:

C++代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int size = nums.size();
        if (size <= 1) return size;
        map<int, int> mp = { {nums[0],1} };
        int i;
        map<int, int>::iterator it;
        for (i = 2; i <= size; i++) {
            int max = 0;//获取nums[i-1]的最长序列
            bool sgn = false;
            for (it = mp.begin(); it != mp.end(); it++) {
                if (it->first < nums[i - 1]) {
                    sgn = true;
                    max = (it->second + 1>max ? it->second + 1 : max);
                }
                else break;
            }
            if (sgn == false) {
                mp[nums[i - 1]] = 1;
            }
            else mp[nums[i - 1]] = max;
        }
        int res=0;
        for (it = mp.begin(); it != mp.end(); it++) {
            res = (res > it->second ? res : it->second);
        }
        return res;
    }
};

参考的高级解法:

C++代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
      int size = nums.size();
      vector<int> dp;
      for (int i = 0; i < size; i++) {
        int index = binarySearch(dp, nums[i]);
        if (index == dp.size()) {
          dp.push_back(nums[i]);
        } else if (dp[index] > nums[i]) {
          dp[index] = nums[i];
        }
      }
      return dp.size();
    }
  
    int binarySearch(const vector<int>& nums, int target) {
      int low = 0, high = nums.size();
      while (low < high) {
        int mid = low + (high - low) / 2;
        if (nums[mid] == target)
          return mid;
        else if (nums[mid] < target)
          low = mid + 1;
        else
          high = mid;
      }
      return low;
    }
};

猜你喜欢

转载自blog.csdn.net/qq_23523409/article/details/84579135