概念导入:
在数组array中,定义F(x)操作: 以O(1)的时间复杂度查找元素 x 左边第一个比它小的元素 l 以及右边第一个比它小的元素 r ,可以通过中心扩展的方式找到,时间复杂度也不会太高,但如果要求数组中所有元素进行F(x)操作呢,又该如何降低复杂度到O(n)呢?
算法定义:
单调栈:通过维护栈内的单调性形成的特殊的栈。
算法特性及作用:
在遍历array的每一个元素时:
第一步,如果遇到第一个比栈顶元素小的值 x ,就开始出栈,直到当前的栈顶元素的值比 x 小,或者栈为空。
第二步:将 x 入栈
可见栈顶元素比 x 大,同时也比栈内的每一个元素大。栈顶上一个元素 l,一定是左边比x 小并且离 x 最近的第一个元素, 如果l 与 x 中间还存在比 l 小的元素,l一定会在 x 入栈前出栈。
作用:用于计算最大柱形面积,查找驼峰使用。
简单题型:每日温度
题目描述:给定一个数组array, 要求返回一个数组answer
,其中 answer[i]
是指下一个比 array[i] 大的元素的索引,如果后面没有比它大的,answer[i] = 0。
个人思路:基本单调栈写法,保持栈内单调递减。
代码实现:
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size(),cnt = 0;
vector<int> ans(n,0); // 先初始化全为零
vector<pair<int,int> > st; // pair.first 存储温度值,pair.second() 存储索引值。
for (auto p : temperatures) {
while (st.size() && st.back().first < p) {
auto k = st.back().second;
ans[k] = cnt - k; // 索引间的距离
st.pop_back();
}
st.push_back({p,cnt});
cnt++; // 当前位置的索引
}
return ans;
}
};
简单题型:移除 k 位整数
题目描述:给定数字字符串,去除其中的 k 位,以字符串的形式返回剩余位数的最小值。
个人思路:字符串中越高位的权值越大,要让剩余数越小,越高位上的数就要尽可能的小。但不管删去哪一位,最终数字的位数还是一样大的,故重点是要保证高位上的数字小即可。列如14781 -> 中 4 比左边的数大,如果删除4 得到1721,同理 7 比左边的数要大,删除7得到1481,发现删除7更合理。8比左边的数要大,删除得到1471,1 比左边的数要小,删除得1478。
发现删除单调递增序列中最大的数带来的效果最好。
不难发现就是遍历过程中比较左侧的数是否比当前数大,大的话就删除该数字,因为删除是一个动态的过程,每删除一个就要重新遍历,所以当采用朴素的算法就是O(n ^ 2)。
因为是找前面最近的一个比当前数小的值,可以用 单调栈 加速这一过程,维护一个单调递增的栈,遍历字符串的每一个数字时:
第一步:如果当前数字比栈顶元素小就持续删除栈顶,直到栈为空或者k (剩余所需删除数)为0。
第二步:将当前数字入栈
特殊处理1:遍历完后,如果k(剩余删除数)还未用完,从末尾继续删除。
特殊处理2:去除前导0,并且字符串若全是0,如"00000",最终返回0.
代码实现:
class Solution {
public:
string removeKdigits(string num, int k) {
vector<char> st;
for (int i = 0; i < num.size(); i++) {
while (st.size() && k && st.back() > num[i]) { // 第一步
st.pop_back();
k--;
}
st.push_back(num[i]); // 第二步
}
for (int i = 0; i < k; i++) { // 如果遍历完字符串,还需要删除数字,就从末尾开始删除
st.pop_back();
}
string ans = "";
// 去除前导0
bool lead = false;
for (auto c : st) {
if (!lead && c == '0') continue;
else {
lead = true;
ans += c;
}
}
// 特判: ans为空时,返回0.
return ans.size() ? ans : "0";
}
};
个人总结:
单调栈只是数据结构的一种,单纯的单调栈并不能解决问题,它的作用在于加速查找该元素左右两边比它大的元素的位置,做题时,应该先想清楚朴素算法在构思能否用单调栈进行加速。