Part1. 我的思路和代码
将栈1的所有元素依次出栈并压入栈2中,栈2元素的顺序就与栈1相反
,即栈2从顶到底的元素就是“从先到后压入栈1”的元素,对栈2进行出栈操作时,效果就如同队列头部的pop操作。具体做法为:push操作时,仅将元素压入stack1;而pop操作时,先将stack1的元素依次出栈并压入stack2中
,stack2的顶部就是队列头部的元素,让其出栈,然后再把stack2的元素依次出栈并压入stack1中
,还原stack1元素本来的顺序。
按此方案编写的代码可以通过,但队列的pop操作中每次都需要把所有元素从stack1转移到stack2,于是时间复杂度为O(n),不是题目要求的O(1)
。
stack<int> stack1;
stack<int> stack2;
void push(int node) {
stack1.push(node);
}
int pop() {
int ans = 0;
while(stack1.empty() == false){ //将栈1的元素转到栈2中
int temp = stack1.top();
stack1.pop();
stack2.push(temp);
}
ans = stack2.top();
stack2.pop();
while(stack2.empty() == false){ //还原栈1的元素
int temp = stack2.top();
stack2.pop();
stack1.push(temp);
}
return ans;
}
Part2. 其他做法
书中给出的做法
我和书中做法不同的地方是:书中将stack1的元素转移到stack2后,没有再将stack2的元素转回到stack1
,而是根据stack2是否为空减少了操作次数。具体地,当进行队列的pop操作时,若stack2为空,则将stack1的元素依次出栈并压入stack2,和我的实现相同;而stack2不为空时,队列进行pop操作时,继续让stack2的栈顶元素出栈即可。队列进行push操作时,同样为仅将元素压入stack1。
这种方式下,队列的pop操作时间复杂度有时为O(n)有时为O(1),但平均考虑下主要为O(1)
。
stack<int> stack1;
stack<int> stack2;
void push(int node) {
stack1.push(node);
}
int pop() {
int ans = 0;
if(stack2.empty()){ //若栈2为空,先将栈1的元素依次出栈并压入栈2
while(stack1.empty() == false){
int temp = stack1.top();
stack1.pop();
stack2.push(temp);
}
}
ans = stack2.top(); //这些语句不是else分支,因为无论栈2是否为空都需要进行此操作
stack2.pop();
return ans;
}
Part3. 相关题目
用两个队列模拟一个栈。
我的想法
定义两个队列q1和q2。栈的push操作中,先将q1中的所有元素依次出队并入队到q2,接着将新元素入队q1,再将q2的所有元素依次出队并入队到q1,在这样的操作后,q1中的元素被倒转
;栈的pop操作中,q1队首元素正常出队即可,因为栈的push操作时q1的元素已经被倒转
。
栈的push操作时间复杂度为O(n),想优化一下
看能不能和“用两个栈实现队列”时从书中学到的做法一样,让多数情况下时间复杂度为O(1),但没有想出来,于是接下来看书上是怎么解释的。
其他做法
书上的做法是:栈的pop操作中,将非空队列除队尾外的元素都入队到另一个队列,剩下的队尾元素就是应该出栈的元素,相当于两个队列交替使用
,时间复杂度也是O(n);栈的push操作中,将新元素入队到非空的那个队列。该做法中时间复杂度高的操作位于栈的pop操作,而我的方法是位于栈的push操作,且由于队列的交替使用,复杂度比我的更低(我需要将近操作两遍完整的队列,而书中的方法仅操作一遍
)。
Part4. 心得体会
- 在之前的“6_从尾到头打印链表”中,我记录到“对stack的用法陌生了些”,这次不得不使用了(
笑
)。 - 读过题目后,想到了大二用的紫白色数据结构书的一张图,那张图就展示了栈和队列的联系,然而已经不记得了。想了一阵子后,得到的是时间复杂度为O(n)的方法。
- 没有看书中的方法时,我以为O(1)复杂度意味着不能有类似“将某个栈的元素全部转移到另一个栈”的操作,于是想了很久也没有思路,看过书中的方法后,得知这样的操作不是每次pop操作时都要进行,多数情况下时间复杂度还是O(1)的,比自己的方法要高效很多。
- 思考相关题目的过程练习了我的
优化意识
。