两个栈占底相邻拼接在一起,入队直接push到右侧栈就行了,出队先看左侧栈有无元素,若无元素需要将右侧栈元素腾到左侧栈中,若有元素直接左侧栈出栈。
class MyQueue {
public:
stack st1, st2;
MyQueue() {
}
void push(int x) {
st2.push(x);
}
int pop() {
if(st1.size()){
int res = st1.top();
st1.pop();
return res;
}
else if(st2.size()){
while(st2.size()){
int num = st2.top();
st2.pop();
st1.push(num);
}
int res = st1.top();
st1.pop();
return res;
}
else{
//非法操作
}
return 0;
}
int peek() {
if(st1.size()){
int res = st1.top();
return res;
}
else if(st2.size()){
while(st2.size()){
int num = st2.top();
st2.pop();
st1.push(num);
}
int res = st1.top();
return res;
}
else{
//非法操作
}
return 0;
}
bool empty() {
return !st1.size() && !st2.size();
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
用两个队列来模拟栈,这两个队列任何时刻都是一个为空,一个非空,然后push元素时push到空队列中,再把非空队列元素依次腾到该队列中,这样接下来出队的时候先出刚push进来的元素。出栈时间复杂度O(1),入栈时间复杂度O(n),可以看到复杂度还是挺高的,而且设计思路没有很巧妙,这题更像是为了对应上一题而出的。
class MyStack {
public:
queue q1, q2;
MyStack() {
}
void push(int x) {
if(q1.size()){
q2.push(x);
while(q1.size()){
q2.push(q1.front());
q1.pop();
}
}
else{
q1.push(x);
while(q2.size()){
q1.push(q2.front());
q2.pop();
}
}
}
int pop() {
if(q1.size()){
int res = q1.front();
q1.pop();
return res;
}
else if(q2.size()){
int res = q2.front();
q2.pop();
return res;
}
else{
//非法操作
}
return 0;
}
int top() {
if(q1.size()){
int res = q1.front();
return res;
}
else if(q2.size()){
int res = q2.front();
return res;
}
else{
//非法操作
}
return 0;
}
bool empty() {
return !q1.size() && !q2.size();
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
需要用到两个栈,其中一个栈正常存放数据,另外一个栈是个辅助栈,用来存放最小值。这两个栈同步进行push和pop操作,push的时候正常栈正常push,辅助栈push的是当前最小值和待入栈值中的最小的那个,这样就维护了当前栈中最小值,由于pop的时候也是同步进行pop的,所以辅助栈的栈顶永远都是正常栈中的最小值。
但是这种做法空间消耗大,网上还有一种只需要一个栈的做法,这个栈只保存第i个元素和前i-1个元素中最小值的差值,同时还有一个全局变量mn维护前i个元素的最小值。这样如果栈顶元素为负数,那说明第i个元素比前i-1个元素都小,mn就存的是第i个元素的值,此时top()函数就应该返回mn,此时pop()函数弹出了最小值,所以还需要更新mn为前i-1个元素的最小值,也就是令mn = mn-栈顶值,此时mn就恢复为前i-1个元素最小值了。如果栈顶值为非负,那说明第i个元素并不会更新最小值,所以top()函数返回mn+栈顶值,pop()函数直接弹栈,不需要更新mn值。
//双栈版
class MinStack {
public:
stack st1, st2;
MinStack() {
st2.push(0x7fffffff);
}
void push(int val) {
st1.push(val);
st2.push(min(val, st2.top()));
}
void pop() {
st1.pop();
st2.pop();
}
int top() {
return st1.top();
}
int getMin() {
return st2.top();
}
};
//单栈版
class MinStack {
public:
stack st;
long long mn = 0x7fffffff;
MinStack() {
}
void push(int val) {
long long diff = val-mn;
st.push(diff);
if(diff < 0) mn = val;
}
void pop() {
if(st.top() > 0) st.pop();
else{
mn = mn-st.top();
st.pop();
}
}
int top() {
if(st.top() > 0) return st.top()+mn;
else return mn;
}
int getMin() {
return mn;
}
};
/**
* Your MinStack object will be instantiated and called as such:
* MinStack* obj = new MinStack();
* obj->push(val);
* obj->pop();
* int param_3 = obj->top();
* int param_4 = obj->getMin();
*/
经典的栈应用,如果遇到左括号就入栈,如果遇到右括号就出栈,出现以下三种情况就判定为不匹配括号:1、遇到右括号但是栈空。2、遇到右括号但和栈顶左括号不是同类括号。3、遍历完括号序列后栈中仍有左括号。
class Solution {
public:
bool check(char a, char b){
if(a == '(' && b == ')') return true;
if(a == '{' && b == '}') return true;
if(a == '[' && b == ']') return true;
return false;
}
bool isValid(string s) {
stack st;
for(int i = 0; i < s.size(); i++){
if(s[i] == '(' || s[i] == '{' || s[i] == '[')
st.push(s[i]);
else{
if(st.empty()) return false;
char top = st.top();
st.pop();
if(!check(top, s[i])) return false;
}
}
if(st.size()) return false;
return true;
}
};
找每个元素右边第一个比它大的,典型的单调栈。可以维护一个单调不增栈,某个元素入栈时弹出栈中所有比它小的元素,这时候就可以更新这些被弹出元素右边第一个比其大的元素了。
class Solution {
public:
vector dailyTemperatures(vector& temperatures) {
stack st;
vector res(temperatures.size());
for(int i = 0; i < temperatures.size(); i++){
while(st.size() && temperatures[i] > temperatures[st.top()]){
res[st.top()] = i;
st.pop();
}
st.push(i);
}
while(st.size()){
res[st.top()] = st.top();
st.pop();
}
for(int i = 0; i < res.size(); i++)
res[i] = res[i]-i;
return res;
}
};
因为字符串里只涉及括号和加减运算,所以这道题实现起来不必中缀转后缀表达式,因为只有加减所以可以直接展开括号,比如5 - ( 3 + 2 ) 可以展为5 - 3 - 2,这里的括号就不具有优先级了,所以整体思路就是从左到右读数字,然后判断它的正负性,再累加起来就行了。如果没有括号,那一个数字的正负完全由前面的符号确定,但有了括号就还需要考虑一点,如果括号前是负号那展开括号后其中的所有符号都会取反,所以就需要一个栈来维护括号前的符号,遇到左括号就把前面符号入栈,遇到右括号就出栈,然后再维护一个变量neg_num记录栈中负号个数。这时候就可以进行正负性判断了,如果遇到了一个正号,那就看下此时neg_num的奇偶性,如果neg_num为偶数那就不变还是正号,如果为奇数那就变号为负。如果遇到的是负号也同理。
由于展开了括号,括号带来的计算优先级也就不存在了,所以可能会出现溢出int范围的情况,题目里说的运算结果在int范围内是在有括号参与的正常运算前提下,比如-2^31 - ( 5 - 6 ),正常运算的话是-2^31 + 1,并不会溢出int,但展开括号就是-2^31 - 5 + 6,这时候就会溢出int了。
class Solution {
public:
int calculate(string s) {
stack st;
long long neg_num = 0, sign = 1, ans = 0;
char last = '+';
for(int i = 0; i < s.size(); i++){
if(s[i] == '+'){
if(neg_num&1) sign = -1;
else sign = 1;
last = s[i];
}
else if(s[i] == '-'){
if(neg_num&1) sign = 1;
else sign = -1;
last = s[i];
}
else if(s[i] == '('){
st.push(last);
if(last == '-') neg_num++;
last = '+';
}
else if(s[i] == ')'){
if(st.top() == '-') neg_num--;
st.pop();
}
else if(s[i] == ' ') continue;
else{
long long num = 0;
while(i < s.size() && s[i] >= '0' && s[i] <= '9'){
num = num*10+s[i]-'0';
i++;
}
i--;
ans += sign*num;
}
}
return ans;
}
};
类似上一道题,但是这道题里没有括号但加入了乘除运算,而且全是非负数进行运算。同样可以用中缀转后缀解决,但是在题目给出的限制条件下有更简单的方法。乘除法优先运算,所以可以把表达式中的乘除先运算完,然后把结果值放回去,最后最近一趟加减运算就行了。具体实现的话开一个栈会比较方便,遇到加号把后面原数入栈,遇到减号把后面相反数入栈,遇到乘除号就让后面原数和栈顶做对应运算,弹栈再结果入栈。
class Solution {
public:
int calculate(string s) {
stack st;
char op = '+';
for(int i = 0; i < s.size(); i++){
if(s[i] == ' ') continue;
else if(s[i] >= '0' && s[i] <= '9'){
long long num = 0;
while(i < s.size() && s[i] >= '0' && s[i] <= '9'){
num = num*10+s[i]-'0';
i++;
}
i--;
if(op == '+') st.push(num);
else if(op == '-') st.push(-num);
else if(op == '*'){
num *= st.top();
st.pop();
st.push(num);
}
else{
num = st.top()/num;
st.pop();
st.push(num);
}
}
else{
op = s[i];
}
}
long long ans = 0;
while(st.size()){
ans += st.top();
st.pop();
}
return ans;
}
};
贪心地想,为了让删除k位后的数字最小,首先得确保靠前的数字尽可能小。可以通过单调栈维护出每个数字右边第一个比它小的数字,然后遍历数字,考虑当前位置是删除还是保留,如果删除的话那必须一直删到比它小位置,否则删除是没有意义的,设当前位置为i,右边第一个比它小的位置为a[i],当满足a[i]-i >= k时是可以删除的,如果不满足这个条件那就只能保留了。
class Solution {
public:
string removeKdigits(string num, int k) {
stack st;
vector a(num.size());
for(int i = 0; i < num.size(); i++){
while(st.size() && num[i] < num[st.top()]){
a[st.top()] = i;
st.pop();
}
st.push(i);
}
while(st.size()){
a[st.top()] = num.size();
st.pop();
}
string ans = "";
bool flag = false;
for(int i = 0; i < a.size(); i++){
if(a[i]-i > k){
if(!flag && num[i] != '0' || flag)
ans += num[i], flag = true;
}
else{
k -= a[i]-i;
i = a[i]-1;
}
}
if(ans == "") ans = "0";
return ans;
}
};