Leetcode解题系列(4)
225 用队列实现栈
题目描述:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-stack-using-queues
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例2:
输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
简单来说,就是要将栈封装成一个队列,让其实现队列的逻辑功能;
栈和队列的区别就是:栈是先进后出,可以类比于现实生活中压子弹的过程,先压入的最后打出来;
而队列就像在食堂排队一样,先进去的先出来,后进去的后出来
队列和栈是两种常用的数据结构:
下面详细介绍这两种数据结构:
队列:先进先出,可以想象成在排队的一个队伍,不过加入元素和提出元素都是人为控制的
常用函数:
//queue队列常用函数
//在c++中#include<queue>就可以使用队列类了
queue<int> q1;//声明一个队列对象
q1.push(1);//在队列尾部插入一个元素
q1.pop();//将最靠前的元素弹出,void型函数,不返回
q1.size();//返回队列元素个数
q1.empty();//判断队列是否为空
q1.front();//返回最早进入队列的元素,但并不剔除
q1.back();//返回最后进入的元素,不做任何操作
栈:元素进出的顺序为先进后出,可以理解为压子弹夹,先压进去的子弹最后打出,后压进去的先打出来
常用函数:
#include<stack>
stack<int> s;
s.push(1);
s.pop();//不返回
s.top();//返回但不弹出
s.empty();
用两个队列实现栈
主要解决的问题是进出的顺序问题。
由于队列和栈的进出顺序不同,我们可以这样想,假设前n-1个队列中的元素的顺序是正确的,现又新push一个元素(第n个元素),这样,这个最后加入的元素需要在队列的最前端(最先进入队列的元素),所以我们可以使用两个队列,每次push要做的事情就是先将一个单独的元素push进一个空队列,然后将另一个队列中的所有元素依次pop出来再push进入这个队列,这样每个过程结束后都有一个空队列和一个有内容的队列,而队列中元素的顺序是和栈相同的,可以直接pop出来。
class MyStack {
public:
/** Initialize your data structure here. */
queue<int> q1,q2;
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
if(q1.empty()){
q1.push(x);
while(!q2.empty()){
q1.push(q2.front());
q2.pop();
}
}else{
q2.push(x);
while(!q1.empty()){
q2.push(q1.front());
q1.pop();
}
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int res=0;
if(!q1.empty()){
res=q1.front();
q1.pop();
return res;
}
if(!q2.empty()){
res=q2.front();
q2.pop();
return res;
}
return res;
}
/** Get the top element. */
int top() {
if(!q1.empty())return q1.front();
if(!q2.empty())return q2.front();
return 0;
}
/** Returns whether the stack is empty. */
bool empty() {
if((q1.empty())&&(q2.empty()))return true;
return false;
}
};
/**
* 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();
*/
代码实现是比较简单的。
主要需要注意的就是,C++中的栈和队列的函数还是有一些差别的,比如队列的pop()不会返回栈顶元素,所以要先用front()得到栈顶元素后再用pop()
接下来用一个队列实现:只要每次push以后,再将前面的元素依次pop出来再push进去,就可以实现栈的逻辑进出:
(有点像首尾循环的意思,自己可以画图看一下)
class MyStack {
public:
queue<int> q;
/** Initialize your data structure here. */
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
q.push(x);
for(int i=0;i<q.size()-1;i++){
q.push(q.front());
q.pop();
}
}
/** Removes the element on top of the stack and returns that element. */
int pop() {
int i=q.front();
q.pop();
return i;
}
/** Get the top element. */
int top() {
return q.front();
}
/** Returns whether the stack is empty. */
bool empty() {
return q.empty();
}
};
/**
* 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();
*/
20、有效的括号
题目描述: 给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
思路 这个匹配括号的过程很容易想到用栈的数据结构来实现
我想到的思路就是每种括号都维护一个栈,当遍历字符串时,每遇到一个左括号时就入栈,遇到右括号时就将左括号出栈,这样到了最后的时候,当三个栈都为空时就表示有效,输出true,若输入右括号时栈就已经空了或到字符串末尾时栈不为空,则表示括号无效,输出false;
代码如下:
class Solution {
public:
bool isValid(string s) {
stack<int> s1;
char curr=s[0];int i=0;
for(i=0;i<s.length();i++){
if(curr=='('){
s1.push(1);
}
if(curr=='{'){
s1.push(2);
}
if(curr=='['){
s1.push(3);
}
if(curr==')'){
if(s1.empty()){
return false;
}
if(s1.top()==1){
s1.pop();
}else return false;
}
if(curr=='}'){
if(s1.empty()){
return false;
}
if(s1.top()==2){
s1.pop();
}else return false;
}
if(curr==']'){
if(s1.empty()){
return false;
}
if(s1.top()==3){
s1.pop();
}else return false;
}
curr=s[i+1];
}
if(s1.empty()){
return true;
}else return false;
}
};
这个方法是可行的
也可以用下面的方法:
class Solution {
public:
bool isValid(string s) {
int n = s.size();
if (n % 2 == 1) {
return false;
}
unordered_map<char, char> pairs = {
{')', '('},
{']', '['},
{'}', '{'}
};
stack<char> stk;
for (char ch: s) {
if (pairs.count(ch)) {
if (stk.empty() || stk.top() != pairs[ch]) {
return false;
}
stk.pop();
}
else {
stk.push(ch);
}
}
return stk.empty();
}
};
155、最小栈
题目描述: 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。
要在常数时间内检索到最小元素,这就说明不能使用getMin()函数才去排序,否则肯定不会是常数时间;
需要在栈外再维护一个空间,记录大小顺序和在站内的位置,这样就算pop一个元素出来,也可以及时找到最小元素;
解析 由于这次需要我们有一个检索最小值的功能,所以我们这次创建两个栈,一个正常的栈xStack,一个用来存储较小值的栈minStack。
我们先给minStack存入一个很大的值——Integer.MAX_VALUE,用来后续的操作。
首先是实现push方法,我们的xStack就进行正常的push操作。我们的minStack则要将我们栈内的
元素和我们要存储的元素作比较,存入较小的值,我们的minStack就总能保证最小值在最顶层;
pop方法是删除栈顶的元素,我们就是直接使用Deque的pop即可,两个栈执行一次pop方法。
top方法我们是获取栈顶元素,我们的主栈xStack执行Deque的peek方法即可;
getMin方法是我们的检索栈中最小元素,我们就可以使用minStack的peek方法来实现;
这个题其实还挺有意思的,乍一看挺难的,但其实用对了方法会非常简单!
代码如下:
class MinStack {
public:
/** initialize your data structure here. */
MinStack() {
minstack.push(INT_MAX);
}
stack<int>minstack,xstack;
void push(int val) {
xstack.push(val);
minstack.push(min(minstack.top(),val));
}
void pop() {
xstack.pop();
minstack.pop();
}
int top() {
return xstack.top();
}
int getMin() {
return minstack.top();
}
};
/**
* 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();
*/
非常简单的几行代码就可以实现这样的功能,很巧妙。自己画图就可以理解为什么可以这样push minstack,就可以达到这样的效果(当pop到xstack中最小的数字时,minstack中的top也发生了改变)。
还有一个要注意的点就是初始化minstack时,要在栈底放入一个非常大的数字,c++中是INT_MAX,这样就不会出错。