您的位置:首页 > 其它

LeetCode 300 最长上升子序列

2020-04-05 18:17 507 查看

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

示例:

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

可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

以下是O(n log n)解法:

[code]class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = nums.size();
if (len < 2) {
return len;
}

vector<int> dp;
for (auto& num : nums) {
auto idx = lower_bound(dp.begin(), dp.end(), num) - dp.begin();
if (idx >= dp.size()) {
dp.emplace_back(num);
} else {
dp[idx] = num;
}
}
return dp.size();
}
};

 

1. lower_bound 指的是 返回第一个 ”大于等于 value“ 的元素位置。

    另一种解释是 可插入”元素值为 value“且 ”不破坏有序性“的第一个位置

2. upper_bound 指的是 返回第一个 “大于 value ” 的元素位置;

    另一种解释是 可插入”元素值为 value“且 ”不破坏有序性“的最后一个位置

举个例子: 1 2 2 3 4 5

value = 2: 则 lower_bound 返回的位置是 第 1 个位置;

                        upper_bound 返回的位置是 第 3 个位置。

大家可以看到一个很有意思的性质:

upper_bound - lower_bound = 数组中 value 的个数

下面我们来研究下 lower_bound 和 upper_bound 的实现。
// return the first element which >= x
int LowerBound(int *a, int n, int x)
{
  int left(0), right(n-1), mid;
  
  while(left <= right){
    mid = left + ((right - left) >> 1);
    if(a[mid] < x){
        left = mid + 1;
    }
    else{
        right = mid - 1;
    }
  }
 
  return left;
}
 
// return first element which > x
int UpperBound(int *a, int n, int x)
{
  int left(0), right(n-1), mid;
 
  while(left <= right){
      mid = left + ((right - left) >> 1);
      if(a[mid] <= x){
          left = mid + 1;
      }
      else{
          right = mid - 1;
      }
  }
 
  return left;
}

 

大家知道,在 STL 库中,区间一般采用半闭半开区间 [begin, last),

这种做法的好处是 last - begin 就是区间元素的个数。

以下是 STL 中的源码:

template <class _ForwardIter, class _Tp, class _Distance>
_ForwardIter __lower_bound(_ForwardIter __first, _ForwardIter __last,
                           const _Tp& __val, _Distance*) 
{
  _Distance __len = 0;
  distance(__first, __last, __len);
  _Distance __half;
  _ForwardIter __middle;
 
  while (__len > 0) {
    __half = __len >> 1;
    __middle = __first;
    advance(__middle, __half);
    if (*__middle < __val) {
      __first = __middle;
      ++__first;
      __len = __len - __half - 1;
    }
    else
      __len = __half;
  }
  return __first;
}

 

emplace_back:

 在容器尾部添加一个元素,这个元素原地构造,不需要触发拷贝构造和转移构造。而且调用形式更加简洁,直接根据参数初始化临时对象的成员。

  • 点赞
  • 收藏
  • 分享
  • 文章举报
fangcao1634 发布了1 篇原创文章 · 获赞 0 · 访问量 147 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: