LeetCode每日一题——768. 最多能完成排序的块 II
即求数组最多分段数,使得每段升序拼接后,和原数组升序排列相同。
如何快速找到对应最优分块区间?
所求分段结果第一个区间里的数和原数组排序后的对应的第一个区间的数一定能够一一映射(下标可不同)
法一:采用哈希表——O(nlogn)
将原数组排序→数组B。
数组A(原数组)中元素值对应哈希表key的value值+1;
数组B(原数组排序后)中元素值对应哈希表key的value值-1;
若某块哈希表结果都为0,则匹配,从哈希表中删除。
维护哈希表的非零元素即可,当非零元素数量为0时,则该块匹配。
//代码如下
class Solution {
public:
// 法二:哈希表unordered_map
int maxChunksToSorted(vector<int>& a) {
//将原数组排序——→数组B
vector<int> b = a;
sort(b.begin(), b.end());
unordered_map<int,int> map;
int cnt = 0;
//从前往后枚举每一个元
for(int i = 0; i < a.size(); i ++)
{
map[a[i]] ++;
map[b[i]] --;
// 如果刚好为0了,删除该元素(代表两者映射)
if(!map[a[i]]) map.erase(a[i]);
if(!map[b[i]]) map.erase(b[i]);
// 判map非零元素,若map非空,cnt++。
if(!map.size()) cnt++;
}
return cnt;
}
};
multiset 有序可重复集合:
class Solution {
public:
int maxChunksToSorted(vector<int>& a) {
int n = a.size();
//将a数组排序后放入b数组中
vector<int> b = a;
sort(b.begin(), b.end());
// 每次保证有序
multiset<int> s1, s2;
int cnt = 0;
//从前往后枚举每一个元
for(int i = 0; i < n; i ++)
{
//每次将a[i]加入到哈希表s1中,将b[i]加入到哈希表s2中
s1.insert(a[i]), s2.insert(b[i]);
//当s1 == s2 时,说明可以分组,记录答案加一,清空两个哈希表
if(s1 == s2) cnt ++, s1.clear(), s2.clear();
}
return cnt;
}
};
法二:单调栈——O(n)
用一个栈维护前面所有分块的max,栈内只维护每块最大值。
- 若遍历到的新元素大于等于栈顶,可以压栈并单独分块(最多分块的最优解)。
- 一但遍历到的新元素比栈顶元素小(说明新元素肯定包含在之前的块内,因此新元素跟栈顶元素时属于同一区间。)
此时,最好的情况就是从当前断开,此时弹栈后寻找此块分区内的最大值做栈顶。
如何弹栈当属于此块的截至位,即截至到上一小块栈顶?弹栈到不小于当前值的栈顶。
// 单调栈——→O(n)
// 时刻维护一个有序栈的栈顶元素,栈内只存储每个分块的最大元素
// 若栈空或者大于等于栈顶压栈
//一旦出现小于于当前栈顶弹栈,弹栈寻找到此块分区的截至位,并且此块内的最大值做栈顶。
class Solution {
public:
int maxChunksToSorted(vector<int>& arr) {
stack<int> stk;
for (auto x:arr) {
int t = x;
while (stk.size() && x < stk.top()) {
t = max(t, stk.top());
stk.pop();
}
stk.push(t);
}
return stk.size();
}
};