算法思想一:动态规划+两次遍历
解题思路:
最长上升子序列的衍生题,所谓最长凸子序列
从左边到峰顶的序列是从左到右的最长上升子序列,从峰顶到右侧的序列是从右到左的最长上升子序列
因此可以采用从左到右计算一次各个子序列的最长上升子序列 l2r(其中l2r[i]表示以i结束的最长上升子序列长度),从右到左计算一次各个子序列的最长上升子序列 r2l,将对应位置的解相加,取最大
注:结果要减去1,因为峰顶包含了2次。
图解:
代码展示:
Python版本
class Solution: def mountainSequence(self , numberList ): # write code here # 左右两个最长上升子序列l2r, r2l l2r, r2l = [1], [1] # 正序计算从左到右的最长上升子序列l2r for i in range(1,len(numberList)): temp = 1 for j in range(len(l2r)): if numberList[i] > numberList[j]: temp = max(temp,l2r[j]+1) l2r.append(temp) # 倒序计算从右到左的最长上升子序列r2l for i in range(len(numberList)-2,-1,-1): temp = 1 for j in range(len(r2l)): if numberList[i] > numberList[-1-j]: temp = max(temp,r2l[j]+1) r2l.append(temp) result = 0 # 将 l2r, r2l 相加减去1,去除峰顶 for i in range(len(l2r)): result = max(result, l2r[i] + r2l[-1-i] - 1) return result
复杂度分析
时间复杂度:N表示数组的长度,从左到右/从右到左计算最长上升子序列都需要时间
空间复杂度:l2r,r2l数组占用空间
算法思想二:动态规划+二分
解题思路:
方法一的时间复杂度较高,因此可以采用二分法实现从左到右最长上升子序列计算、从右到左最长上升子序列计算
1、定义, 分别表示从左到右最长上升子序列长度、从右到左最长上升子序列长度
2、继承经典的二分求LIS,遍历每个元素的时候,记录当前维护的序列的长度,也就是以这个元素为结尾的元素的长度,然后倒着求一遍
3、最后将left right将对应的位置相加,再减去一个山峰的数量(取最大值)
代码展示:
C++版本
class Solution { public: /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * 返回最大山峰序列长度 * @param numberList int整型vector 给定的序列 * @return int整型 */ int mountainSequence(vector<int>& numberList) { // write code here // 初始化 int n = numberList.size(), ret = 1; // 从左边最长递增子序列left(n), 从右边最长递增子序列 right(n) // dpl, dpr 辅助数组 vector<int>left(n), right(n), dpl, dpr; // 一次遍历 for (int i = 0; i < n; i++) { // lower_bound 二分查找函数 // 正向 auto itl = lower_bound(dpl.begin(), dpl.end(), numberList[i]); // 反向 auto itr = lower_bound(dpr.begin(), dpr.end(), numberList[n - 1 - i]); // 正向遍历判别 if (itl == dpl.end()) dpl.push_back(numberList[i]); else *itl = numberList[i]; // 反向遍历判别 if (itr == dpr.end()) dpr.push_back(numberList[n - 1 - i]); else *itr = numberList[n - 1 - i]; left[i] = dpl.size(); right[n - 1 - i] = dpr.size(); } // 最后循环更新最大值 for (int i = 0; i < n; i++) // left right将对应的位置相加,再减去一个山峰的数量(取最大值) ret = max(ret, left[i] + right[i] - 1); return ret; } };
复杂度分析
时间复杂度:N表示数组的长度,最外层遍历时间,内层二分计算时间
空间复杂度:left,right占用空间