本题的其它解法
题目
我们定义 arr 是 山形数组 当且仅当它满足:
arr.length >= 3
存在某个下标 i (从 0 开始) 满足 0 < i < arr.length - 1 且:
arr[0] < arr[1] < … < arr[i - 1] < arr[i]
arr[i] > arr[i + 1] > … > arr[arr.length - 1]
给你整数数组 nums ,请你返回将 nums 变成 山形状数组 的 最少 删除次数。
示例 1:
输入:nums = [1,3,1]
输出:0
解释:数组本身就是山形数组,所以我们不需要删除任何元素。
示例 2:
输入:nums = [2,1,1,5,6,2,3,1]
输出:3
解释:一种方法是将下标为 0,1 和 5 的元素删除,剩余元素为 [1,5,6,3,1] ,是山形数组。
参数范围:
3 <= nums.length <= 1000
1 <= nums[i] <= 109
题目保证 nums 删除一些元素后一定能得到山形数组。
分析
本题可以转换成:最长山形数组,再进一步转换成最长升序子序列。
时间复杂度
时间复杂度O(nlogn)。分两步:一,寻找左半部分。二,寻找右半部分。每步枚举每个山顶,时间复杂度O(n),每个山顶二分查找一次,时间复杂度O(logn)。
vLenToMin
vLenToMin[i]的含义是 长度为i+1 的子序列 的结尾,如果有多个符合的子序列,取结尾最小的。
比如:
原始数组 | vLenToMin |
1 | 1 |
1 2 | {1}->{1,2} |
2 1 | {2}->{1} |
1 2 3 | {1}->{1,2 |
1 3 3 4 5 | {1}->{1,3}->{1,3,4}->{1,3,4,5} |
1 3 5 4 | {1}->{1,3}->{1,3,5}->{1,3,4} |
总结
一,只会在尾部增加元素。不会在其它位置增加元素。
二,不会删除元素。
三,会替换元素。
四,严格递增。
五,所有的数都小于当前值时,在末尾增加,显然是升序。
六,it第一个大于等于n的迭代器,之前的元素一定严格小于it,而it<=n,故前面元素一定小于n。
后面的元素不会ij等于it,否则它就是*it。[ii,…)都大于等于n,所有ij不会小于n。
七,规则五和六保证了vLenToMin永远严格递增。
代码
核心代码
class Solution { public: int minimumMountainRemovals(vector<int>& nums) { vector<int> vLeftLen,vRightLen; Do(vLeftLen, nums); Do(vRightLen, vector<int>(nums.rbegin(), nums.rend())); std::reverse(vRightLen.begin(), vRightLen.end()); int iMaxLen = 0; for (int i = 1; i+1 < nums.size(); i++) { if ((vLeftLen[i] > 1) && (vRightLen[i] > 1)) { iMaxLen = max(iMaxLen, vLeftLen[i] + vRightLen[i] - 1); } } return nums.size() - iMaxLen; } void Do(vector<int>& vLen, const vector<int> nums) { vector<int> vLenToMin;//vLenToMin[i]的含义是 长度为i+1 的子序列 的结尾,如果有多个符合的子序列,取结尾最小的。 for (const auto& n : nums) { auto it = std::lower_bound(vLenToMin.begin(), vLenToMin.end(), n); vLen.emplace_back(it - vLenToMin.begin() + 1); if (vLenToMin.end() == it) { vLenToMin.emplace_back(n); } else { if (n < *it) { *it = n; } } } } };
测试用例
template void Assert(const T& t1, const T& t2) { assert(t1 == t2); } template void Assert(const vector& v1, const vector& v2) { if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i < v1.size(); i++) { Assert(v1[i], v2[i]); } } int main() { vector nums; int res; { Solution slu; nums = { 1,3,1 }; res = slu.minimumMountainRemovals(nums); Assert(0, res); } { Solution slu; nums = { 2, 1, 1, 5, 6, 2, 3, 1 }; res = slu.minimumMountainRemovals(nums); Assert(3, res); } { Solution slu; nums = { 9, 8, 1, 7, 6, 5, 4, 3, 2, 1 }; res = slu.minimumMountainRemovals(nums); Assert(2, res); } { Solution slu; nums = { 100, 92, 89, 77, 74, 66, 64, 66, 64 }; res = slu.minimumMountainRemovals(nums); Assert(6, res); } { Solution slu; nums = { 1, 2, 1, 3, 4, 4 }; res = slu.minimumMountainRemovals(nums); Assert(3, res); }
//CConsole::Out(res);
}
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快
速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想对大家说的话 |
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |