目录
3. Implement Stack using Queues
1.Evaluate Reverse Polish Notation(逆置波兰表达式)
2.Largest Rectangle in Histogram
栈的题目需要注意:每次top和pop前都要对栈进行检测
栈一般的使用情况是要找出成对的东西时,或者要根据后面的内容对前面进行操作时
一、esay
1.Min Stack
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
- push(x) -- Push element x onto stack.
- pop() -- Removes the element on top of the stack.
- top() -- Get the top element.
- getMin() -- Retrieve the minimum element in the stack.
Example:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> Returns -3.
minStack.pop();
minStack.top(); --> Returns 0.
minStack.getMin(); --> Returns -2.
思路:这道最小栈跟原来的栈相比就是多了一个功能,可以返回该栈的最小值。使用两个栈来实现,一个栈来按顺序存储push进来的数据,另一个用来存出现过的最小值。
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
}
void push(int x) {
if(s2.empty() || s2.top() >= x) s2.push(x);
s1.push(x);
}
void pop() {
if(s1.top() == s2.top()) s2.pop();
s1.pop();
}
int top() {
return s1.top();
}
int getMin() {
return s2.top();
}
private:
stack<int> s1,s2;
};
2.Valid Parentheses
Given a string containing just the characters '('
, ')'
, '{'
, '}'
, '['
and ']'
, determine if the input string is valid.
An input string is valid if:
- Open brackets must be closed by the same type of brackets.
- Open brackets must be closed in the correct order.
Note that an empty string is also considered valid.
Example 1:
Input: "()"
Output: true
Example 2:
Input: "()[]{}"
Output: true
Example 3:
Input: "(]"
Output: false
Example 4:
Input: "([)]"
Output: false
Example 5:
Input: "{[]}"
Output: true
思路:这题不难,使用栈就可以解决,需要注意的是,最后对于栈为空的判断,为空才为true
class Solution {
public:
bool isValid(string s) {
stack<char> s1;
for(char c:s){
if(c=='(' || c=='{' || c=='[') s1.push(c);
else{
// 注意这一步的检查
if(s1.empty()) return false;
char c2 = s1.top();
if((c==')' && c2=='(') || (c==']' && c2=='[') || (c=='}' && c2=='{')) s1.pop();
else return false;
}
}
// 注意检查
return s1.empty();
}
};
3. Implement Stack using Queues
Implement the following operations of a stack using queues.
- push(x) -- Push element x onto stack.
- pop() -- Removes the element on top of the stack.
- top() -- Get the top element.
- empty() -- Return whether the stack is empty.
用队列来模拟栈,这里其实无论怎样写,每次栈pop的时候都对需要重载一次,复杂度为n
我的写法:用了两个栈
class MyStack {
public:
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
if(q1.empty()) q2.push(x);
else q1.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int res;
if(q1.empty()){
while(!q2.empty()) {
res = q2.front();
q2.pop();
if(!q2.empty()) q1.push(res);
}
return res;
}
else{
while(!q1.empty()) {
res = q1.front();
q1.pop();
if(!q1.empty()) q2.push(res);
}
return res;
}
}
/** Get the top element. */
int top() {
int res;
if(q1.empty()){
while(!q2.empty()) {
res = q2.front();
q1.push(res);
q2.pop();
}
return res;
}
else{
while(!q1.empty()) {
res = q1.front();
q2.push(res);
q1.pop();
}
return res;
}
}
/** Returns whether the stack is empty. */
bool empty() {
if(q1.empty() && q2.empty()) return true;
else return false;
}
private:
queue<int> q1,q2;
};
网上的写法,其实可以优雅使用一个栈
class MyStack {
public:
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
q.push(x);
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int res;
for(int i = 0;i < q.size()-1;i++){
res = q.front();
q.push(res);
q.pop();
}
res = q.front();
q.pop();
return res;
}
/** Get the top element. */
int top() {
int res;
for(int i = 0;i < q.size();i++){
res = q.front();
q.push(res);
q.pop();
}
return res;
}
/** Returns whether the stack is empty. */
bool empty() {
if(q.empty()) return true;
else return false;
}
private:
queue<int> q;
};
二、Mid
1.Evaluate Reverse Polish Notation(逆置波兰表达式)
Evaluate the value of an arithmetic expression in Reverse Polish Notation.
Valid operators are +
, -
, *
, /
. Each operand may be an integer or another expression.
Note:
- Division between two integers should truncate toward zero.
- The given RPN expression is always valid. That means the expression would always evaluate to a result and there won't be any divide by zero operation.
Example 1:
Input: ["2", "1", "+", "3", "*"]
Output: 9
Explanation: ((2 + 1) * 3) = 9
Example 2:
Input: ["4", "13", "5", "/", "+"]
Output: 6
Explanation: (4 + (13 / 5)) = 6
Example 3:
Input: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
Output: 22
Explanation:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
思路:注意是把计算的中间结果继续入栈,而不是在外面判断,开始给我整晕了...
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> s;
int res = 0,m,n;
if(tokens.size() == 1) return stoi(tokens[0]);
for(int i = 0;i < tokens.size();i++){
if (tokens[i] != "+" && tokens[i] != "-" && tokens[i] != "*" && tokens[i] != "/") {
s.push(stoi(tokens[i]));
}
else{
n = s.top();
s.pop();
m = s.top();
s.pop();
if(tokens[i] == "/") res = m/n;
else if(tokens[i] == "*") res = m*n;
else if(tokens[i] == "+") res = m+n;
else if(tokens[i] == "-") res = m-n;
s.push(res);
}
}
return s.top();
}
};
三、hard
1.Longest Valid Parentheses
Given a string containing just the characters '('
and ')'
, find the length of the longest valid (well-formed) parentheses substring.
Example 1:
Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"
Example 2:
Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"
思路:这题确实比较难,我没考虑到的问题是“(()(()”和“)())()”这两种情况,即对于中间被打断,应该怎样处理,单纯使用栈,只能统计合法()的个数,但是统计不了连续合法括号的个数。参考网上的写法,对于多余 ) 的情况,使用一个start来作为起始位置记录,start=i+1;对于多余 ( 的情况,用栈顶的 i 下标作为起始。
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> s1;
int start = 0,max_cnt=0;
for(int i = 0;i < s.size();i++){
if(s[i]=='(') s1.push(i);
else if(s[i]==')'){
if(s1.empty()) start = i+1;
else {
s1.pop();
// s1为空时,使用start作为起始,非空时使用栈顶作为起始
max_cnt = s1.empty()? max(max_cnt,i-start+1):max(max_cnt,i-s1.top());
}
}
}
return max_cnt;
}
};
2.Largest Rectangle in Histogram
Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3]
.
The largest rectangle is shown in the shaded area, which has area = 10
unit.
Example:
Input: [2,1,5,6,2,3]
Output: 10
思路:这题我真的除了暴力破解没啥思路,下面这种方法是参考网上写的剪枝
遍历数组,每找到一个局部峰值(而不是每次都往前查找,这里做了剪枝,因为局部峰值已经是局部最高点,所以往前计算获得的面积是局部最大的,我们比较的是每次的局部峰值处算出的面积到底哪次最大),然后向前遍历所有的值,算出共同的矩形面积,每次对比保留最大值
算出共同的矩形面积不要在heights[i+1]<heights[i]在内部判断后计算,否则如果是持续递增的直方图,就会失去计算机会
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int start = 0,res=0,s,now_max,minH;
for(int i = 0;i < heights.size();i++){
// 这里注意i从0开始,与后一位比较,从1开始会略过heights=[1]这种情况
// 同时判断i是否到末尾,到末尾则往下计算面积
//而不是heights[i+1]<heights[i]在内部判断,否则如果是持续递增的直方图,就会失去计算机会
if(i+1 < heights.size() && heights[i+1]>=heights[i]){
continue;
}
now_max = 0;
minH = heights[i];
for(int j = i;j >= 0;j--){
minH = min(minH,heights[j]);
s = (i-j+1)*minH;
now_max = s>now_max? s:now_max;
}
res = res>now_max? res:now_max;
start = i+1;
}
return res;
}
};
维护一个栈,用来保存递增序列(这样往回计算的时候,肯定是比前一个小的),相当于上面那种方法的找局部峰值。我们可以看到,直方图矩形面积要最大的话,需要尽可能的使得连续的矩形多,并且最低一块的高度要高。有点像木桶原理一样,总是最低的那块板子决定桶的装水量。那么既然需要用单调栈来做,首先要考虑到底用递增栈,还是用递减栈来做。我们想啊,递增栈是维护递增的顺序,当遇到小于栈顶元素的数就开始处理,而递减栈正好相反,维护递减的顺序,当遇到大于栈顶元素的数开始处理。那么根据这道题的特点,我们需要按从高板子到低板子的顺序处理,先处理最高的板子,宽度为1,然后再处理旁边矮一些的板子,此时长度为2,因为之前的高板子可组成矮板子的矩形 ,因此我们需要一个递增栈,当遇到大的数字直接进栈,而当遇到小于栈顶元素的数字时,就要取出栈顶元素进行处理了,那取出的顺序就是从高板子到矮板子了,于是乎遇到的较小的数字只是一个触发,表示现在需要开始计算矩形面积了,为了使得最后一块板子也被处理,这里用了个小trick,在高度数组最后面加上一个0,这样原先的最后一个板子也可以被处理了。由于栈顶元素是矩形的高度,那么关键就是求出来宽度,那么跟之前那道Trapping Rain Water一样,单调栈中不能放高度,而是需要放坐标。由于我们先取出栈中最高的板子,那么就可以先算出长度为1的矩形面积了,然后再取下一个板子,此时根据矮板子的高度算长度为2的矩形面积,以此类推,知道数字大于栈顶元素为止,再次进栈
单调栈的套路,对后面的数来说,前面的序列的“坎”(其他题里面也可能是“坑”)是无效的信息,每次更新栈都可以弹出去,同时这个题里面,被弹出去的“坎”能形成的最大面积正好就是 (j - i) * height
class Solution {
public:
int largestRectangleArea(vector<int> &height) {
int res = 0;
stack<int> st;
height.push_back(0);
for (int i = 0; i < height.size(); ++i) {
if (st.empty() || height[st.top()] < height[i]) {
st.push(i);
} else {
int cur = st.top(); st.pop();
res = max(res, height[cur] * (st.empty() ? i : (i - st.top() - 1)));
--i;
}
}
return res;
}
};
维护一个递减栈,遍历高度,如果此时栈为空,或者当前高度小于等于栈顶高度,则把当前高度的坐标压入栈,注意我们不直接把高度压入栈,而是把坐标压入栈,这样方便我们在后来算水平距离。当我们遇到比栈顶高度大的时候,就说明有可能会有坑存在,可以装雨水。此时我们栈里至少有一个高度,如果只有一个的话,那么不能形成坑,我们直接跳过,如果多余一个的话,那么此时把栈顶元素取出来当作坑,新的栈顶元素就是左边界,当前高度是右边界,只要取二者较小的,减去坑的高度,长度就是右边界坐标减去左边界坐标再减1,二者相乘就是盛水量啦
class Solution {
public:
int trap(vector<int>& height) {
stack<int> s;
int res = 0,i = 0,now,bound;
// 这里不用for循环,因为只有在push以后才需要i++
while(i < height.size()){
if(s.empty() || height[i] <= height[s.top()]) s.push(i++);
else{
now = s.top();
s.pop();
// 注意这里栈空的判断,空就不继续计算
if(s.empty()) continue;
// 栈顶和i处较小的那个
bound = min(height[i],height[s.top()]);
res += (bound - height[now]) * (i-s.top()-1);
}
}
return res;
}
};