LeetCode232.用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(
push
、pop
、peek
、empty
):实现
MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top
,peek/pop from top
,size
, 和is empty
操作是合法的。- 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
思路:想要用栈实现队列,首先要清楚栈和队列的各自工作方式。
栈是后进先出的,队列是先进先出的。于是想要用两个栈来模拟队列,可以采用下面的方法。
首先两个栈一个为进栈,一个为出栈,进出是因为有元素的push以及pop操作。push操作简单,直接将元素push入进栈即可,但是对于出栈,当要删除元素时,如果此时出栈为空,则需要将进栈的所有已入栈元素依次放入出栈中,这样就能在出栈中按照队列先进先出的原则进行。对于返回头部元素,其实与pop函数操作的逻辑很像,所以直接复用pop函数即可,注意需要将已删除元素加上去保持原状。而empty函数的判断则是看两个模拟队列的栈是否同时为空,同时为空则为空,否则不为空。
stack<int> stIn;
stack<int> stOut;//设置两个栈模拟过程
MyQueue() {
//这里可以自定义一些属性
}
void push(int x) {
stIn.push(x);
}
int pop() {
if(stOut.empty()){
while(!stIn.empty()){
int tmp = stIn.top();
stIn.pop();
stOut.push(tmp);
}
}
int val = stOut.top();
stOut.pop();
return val;
}
int peek() {
int val = this -> pop();//这里复用了之前已经定义好的pop函数
stOut.push(val);//上面的获取元素操作将元素弹出来了,但是本身peek函数是查看队列的第一个元素,因此需要将其加回
return val;
}
bool empty() {
return (stIn.empty() && stOut.empty());
}
时间复杂度:pop函数和peek函数是O(n),push函数和empty函数是O(1)
空间复杂度:O(n)
LeetCode225.用队列实现栈
请你仅使用两个队列实现一个后入先出(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(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
思路:对于使用队列来模拟栈,有两种方法,一种是使用题目中所说的两个队列来模拟,还有一种是使用一个队列来模拟,但是总体上来说,两者的逻辑是统一的。
1、两个队列模拟栈
使用两个队列模拟栈,此时不再分为进队列和出队列,而是主队列和辅助队列。
主要还是对pop函数的实现,当出现pop删除元素时,将主队列中的不包含最后一个元素的所有元素依次从头部开始放到辅助队列里,这样当主队列剩下最后一个元素时,即为pop需要删除的元素,删除完成后,将辅助队列中的元素重新放入主队列,对辅助队列中的元素进行删除,即完成了栈顶元素的pop操作。
queue<int> que1;
queue<int> que2; //设置模拟的两个队列
MyStack() {
//这里可以自定义一些属性
}
void push(int x) {
que1.push(x);
}
int pop() {
int size = que1.size();
size --;
while(size --){
que2.push(que1.front());
que1.pop(); //将除了最后一个元素之外的其他元素放入que2中
}
int val = que1.front();//获得了栈顶元素
que1.pop();
que1 = que2; //将que1恢复剩余元素状态
while(!que2.empty()){
que2.pop(); //清空辅助数组que2
}
return val;
}
int top() {
int val = this -> pop();//处理逻辑相似,因此复用了pop函数
que1.push(val);//因为只是查询,所以需要将删除的元素加回去
return val;
//直接写 return que1.back(); 也能过
}
bool empty() {
return que1.empty();
}
时间复杂度:pop函数和top函数是O(n),push函数和empty函数是O(1)
空间复杂度:O(n)
2、一个队列模拟栈
使用一个队列模拟栈,主要还是pop函数的实现。
当进行pop操作时,将除了最后一个元素的前面其他元素依次重新放入队列,这样就会使得之后的队头元素即为所模拟的栈的栈顶元素,获得其值并且将其pop点,即将栈顶元素删除,当继续进行pop操作时,重复上面的操作即可。
queue<int> que;
MyStack() {
//这里可以自定义一些属性
}
void push(int x) {
que.push(x);
}
int pop() {
int size = que.size();
size --;
while(size --){
que.push(que.front());
que.pop(); //将除了最后一个元素之外的其他元素重新放入que中
}
int val = que.front();//获得了对应的栈顶元素
que.pop();
return val;
}
int top() {
int val = this -> pop();//处理逻辑相似,因此复用了pop函数
que.push(val);//因为只是查询,所以需要将删除的元素加回去
return val;
//return que.back(); //直接写也能过
}
bool empty() {
return que.empty();
}
时间复杂度:pop函数和top函数是O(n),push函数和empty函数是O(1)
空间复杂度:O(n)
LeetCode20.有效的括号
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
思路 :栈是应用于匹配问题的最好解决办法。这里提供两种方法,一种是辅助数组法,一种是情况列举法。
1、辅助数组法
根据题意,将括号放入一个map中,键key为括号,值为某个数字,思路呢就是遍历字符串,当栈不为空时,尝试将栈顶元素和所遍历的元素的map值相加,如果等于0,那么说明它们是配对的,但是这还不能说明它们顺序是正确的(也就是左括号出现在右括号之前),因为我之前将所有的左括号的map值设为小于0,所以此时判断栈顶元素的map值是否小于0,如果是,匹配成功,继续遍历,如此循环。当遍历完后,如果说此时栈为空,说明所有括号全部匹配成功,于是返回true;反之,如果存在元素,不为空,说明还有一些括号没匹配上,则返回false。
bool isValid(string s) {
stack<char> st;
unordered_map<char, int> mp;
mp['('] = -1;
mp[')'] = 1;
mp['{'] = -2;
mp['}'] = 2;
mp['['] = -3;
mp[']'] = 3; //将配对的括号分别设置对应的值
for(int i = 0; i < s.size(); i ++){
if(!st.empty()){
if(mp[s[i]] + mp[st.top()] == 0 && mp[st.top()] < 0){
//当mp对应值相等时,说明能配对
//当栈中的括号mp值小于0时,说明是左括号,那么相对的,进来的就是右括号,
//由此才能在真正意义上配对
st.pop();
continue;
}
}
st.push(s[i]);
}
if(st.empty()) return true;
//当栈st为空时,说明括号全部配对成功,返回true
//否则不为空的时候返回false
return false;
时间复杂度:O(n)
空间复杂度:O(n)
2、情况列举法
其实仔细分析一下所有匹配失败的情况,主要有三种:
1、右括号多余或者右括号出现在了左括号之前;
2、括号不匹配;
3、左括号多余。
此题我们采用了一些技巧,当遍历时为左括号时,全部换成其配对的右括号入栈,这样能够方便进行后续操作。
当第一种情况发生时,会使得遍历到该元素时,栈中已经为空,此时直接返回false即可;当第二种情况发生时,即所遍历的该元素与栈顶的元素不相等,直接返回false即可;当第三种情况发生时,是在遍历结束过后,当栈里面仍然还有元素,不为空时,说明多余的左括号没右括号匹配,此时也是直接返回false。当然很明显,当循环过程中没有返回false,循环自然结束,栈为空时,此时就认为各个括号都匹配成功了,直接返回true。
bool isValid(string s) {
if(s.size() % 2 != 0) return false; //s字符串的长度为奇数,肯定没办法匹配
stack<char> st;
for(int i = 0; i < s.size(); i ++){
if(s[i] == '('){
st.push(')');
}else if(s[i] == '{'){
st.push('}');
}else if(s[i] == '['){
st.push(']');
}else if(st.empty() || s[i] != st.top()){
//当还有元素但是栈为空时,说明右括号多余了或者说是先于匹配的左括号出来了;
//当还有元素但是与栈顶元素不匹配时,说明此时括号不相匹
return false;
}else if(s[i] == st.top()){
st.pop();
}
}
if(st.empty()) return true;
return false;//当循环结束,栈里面还有元素时,说明左括号多余了
}
时间复杂度:O(n)
空间复杂度:O(n)
LeetCode1047.删除字符串中的所有相邻重复项
给出由小写字母组成的字符串
S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
思路:当遇到需要对之前已经遍历过的元素进行判断的时候,栈也能够提供很好的解决方案。
因为栈是一个先进后出的数据结构,如果将刚遍历的元素放入栈,那么该元素在栈顶,方便查找,方便比较。
于是这道题的思路就很清晰了,循环遍历字符串s,当栈不为空时,对所遍历的元素与栈顶元素比较,如果元素相等,那么就是相邻的重复项,于是需要将栈顶元素删除,然后继续对后面的元素进行遍历,如果不是相邻重复项则将其压入栈中,如此循环。
当循环结束后,栈中元素即为非相邻重复项,此时用一个string字符串将里面的元素统计出来即可,但是需要注意字符的顺序,因为从栈里面导出必然会是倒叙,所以需要进行顺序的一个置换,之后返回即可。下面有两种导出的方式。
1、将字符加到result前面
string removeDuplicates(string s) {
stack<char> st;
for(int i = 0; i < s.size(); i ++){
if(!st.empty()){
if(s[i] == st.top()){
//当栈不为空的时候,此时如果s[i]与栈顶元素相等,
//那么就依据题意将其去除
st.pop();
continue;
}
}
st.push(s[i]);
}
string result;
while(!st.empty()){
char tmp = st.top();
//注意栈里面的元素如果是从栈顶开始输出
//和原字符串的顺序是反的,所以这里是将元素加在前面
result = tmp + result;
st.pop();
}
return result;
}
时间复杂度:O(n)
空间复杂度:O(n)
2、使用reverse函数
string removeDuplicates(string s) {
stack<char> st;
for(int i = 0; i < s.size(); i ++){
if(!st.empty()){
if(s[i] == st.top()){
//当栈不为空的时候,此时如果s[i]与栈顶元素相等,
//那么就依据题意将其去除
st.pop();
continue;
}
}
st.push(s[i]);
}
string result;
while(!st.empty()){
//注意栈里面的元素如果是从栈顶开始输出,是逆序的
result += st.top();
st.pop();
}
reverse(result.begin(), result.end());//将其倒置
return result;
}
时间复杂度:O(n)
空间复杂度:O(n)
当然这里还有一种方法,那就是将string自身作为栈,这样就能减少创建栈所需的空间复杂度,如下图所示。
string removeDuplicates(string s) {
string result;
for(int i = 0; i < s.size(); i ++){
if(!result.empty()){
if(s[i] == result.back()){
result.pop_back();
continue;
}
}
result.push_back(s[i]);
}
return result;
}
时间复杂度:O(n)
空间复杂度:O(1)(result作为返回值是不计入空间复杂度的)
感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。