09、第五章 栈与队列part01

本节内容

  • 理论基础
  • 232.用栈实现队列
  • 225. 用队列实现栈

理论基础※

了解一下 栈与队列的内部实现机智,文中是以C++为例讲解的。

文章讲解:
https://programmercarl.com/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html

https://www.yuanql.top/2023/07/03/02_leetcode/%E6%A0%88%E4%B8%8E%E9%98%9F%E5%88%97%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80/

232.用栈实现队列※

建议:大家可以先看视频,了解一下模拟的过程,然后写代码会轻松很多。

题目链接: https://leetcode.cn/problems/implement-queue-using-stacks/
文章讲解: https://programmercarl.com/0232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97.html

https://www.yuanql.top/2023/07/03/02_leetcode/232.%20%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97/

题目分析



方案一

曾经做过,有相关的印象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class MyQueue {

private Deque<Integer> leftDeque;

private Deque<Integer> rigthDeque;
public MyQueue() {
leftDeque = new LinkedList<>();
rigthDeque = new LinkedList<>();
}

public void push(int x) {
rigthDeque.push(x);
}

public int pop() {
if (leftDeque.isEmpty()) {
while (!rigthDeque.isEmpty()) {
leftDeque.push(rigthDeque.pop());
}
}
return leftDeque.pop();
}

public int peek() {
if (leftDeque.isEmpty()) {
while (!rigthDeque.isEmpty()) {
leftDeque.push(rigthDeque.pop());
}
}
return leftDeque.peek();
}

public boolean empty() {
return leftDeque.isEmpty() && rigthDeque.isEmpty();
}
}

结果

解答成功:
执行耗时:0 ms,击败了100.00% 的Java用户
内存消耗:39.2 MB,击败了83.29% 的Java用户

代码随想录

https://programmercarl.com/0232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97.html

这是一道模拟题,不涉及到具体算法,考察的就是对栈和队列的掌握程度。

使用栈来模式队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈,一个输出栈,这里要注意输入栈和输出栈的关系。

下面动画模拟以下队列的执行过程:

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class MyQueue {

Stack<Integer> stackIn;
Stack<Integer> stackOut;

/** Initialize your data structure here. */
public MyQueue() {
stackIn = new Stack<>(); // 负责进栈
stackOut = new Stack<>(); // 负责出栈
}

/** Push element x to the back of queue. */
public void push(int x) {
stackIn.push(x);
}

/** Removes the element from in front of queue and returns that element. */
public int pop() {
dumpstackIn();
return stackOut.pop();
}

/** Get the front element. */
public int peek() {
dumpstackIn();
return stackOut.peek();
}

/** Returns whether the queue is empty. */
public boolean empty() {
return stackIn.isEmpty() && stackOut.isEmpty();
}

// 如果stackOut为空,那么将stackIn中的元素全部放到stackOut中
private void dumpstackIn(){
if (!stackOut.isEmpty()) return;
while (!stackIn.isEmpty()){
stackOut.push(stackIn.pop());
}
}
}

225. 用队列实现栈※

建议:可以大家惯性思维,以为还要两个队列来模拟栈,其实只用一个队列就可以模拟栈了。 

建议大家掌握一个队列的方法,更简单一些,可以先看视频讲解

题目链接: https://leetcode.cn/problems/implement-stack-using-queues/
文章讲解: https://programmercarl.com/0225.%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88.html

https://www.yuanql.top/2023/07/04/02_leetcode/225.%20%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88/

题目分析



方案一

曾经做过,有相关的印象

仅仅使用了一个队列实现的,但是感觉时间复杂度有点高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MyStack {

private Deque<Integer> deque;

private int length = 0;

public MyStack() {
deque = new LinkedList<>();
}

public void push(int x) {
deque.offer(x);
for (int i = 0; i < length; i++) {
deque.offer(deque.poll());
}
length++;
}

public int pop() {
length--;
return deque.poll();
}

public int top() {
return deque.peek();
}

public boolean empty() {
return length == 0;
}
}

结果

解答成功:
执行耗时:0 ms,击败了100.00% 的Java用户
内存消耗:39.4 MB,击败了38.71% 的Java用户

代码随想录

https://programmercarl.com/0225.%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88.html

队列模拟栈,其实一个队列就够了,那么我们先说一说两个队列来实现栈的思路。

队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序。

所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。

但是依然还是要用两个队列来模拟栈,只不过没有输入和输出的关系,而是另一个队列完全用来备份的!

如下面动画所示,用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。

模拟的队列执行语句如下:

1
2
3
4
5
6
7
8
9
queue.push(1);        
queue.push(2);
queue.pop(); // 注意弹出的操作
queue.push(3);
queue.push(4);
queue.pop(); // 注意弹出的操作
queue.pop();
queue.pop();
queue.empty();

优化

一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了。

代码实现

使用两个 Queue 实现方法1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class MyStack {

Queue<Integer> queue1; // 和栈中保持一样元素的队列
Queue<Integer> queue2; // 辅助队列

/** Initialize your data structure here. */
public MyStack() {
queue1 = new LinkedList<>();
queue2 = new LinkedList<>();
}

/** Push element x onto stack. */
public void push(int x) {
queue2.offer(x); // 先放在辅助队列中
while (!queue1.isEmpty()){
queue2.offer(queue1.poll());
}
Queue<Integer> queueTemp;
queueTemp = queue1;
queue1 = queue2;
queue2 = queueTemp; // 最后交换queue1和queue2,将元素都放到queue1中
}

/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue1.poll(); // 因为queue1中的元素和栈中的保持一致,所以这个和下面两个的操作只看queue1即可
}

/** Get the top element. */
public int top() {
return queue1.peek();
}

/** Returns whether the stack is empty. */
public boolean empty() {
return queue1.isEmpty();
}
}

使用两个 Queue 实现方法2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class MyStack {
//q1作为主要的队列,其元素排列顺序和出栈顺序相同
Queue<Integer> q1 = new ArrayDeque<>();
//q2仅作为临时放置
Queue<Integer> q2 = new ArrayDeque<>();

public MyStack() {

}
//在加入元素时先将q1中的元素依次出栈压入q2,然后将新加入的元素压入q1,再将q2中的元素依次出栈压入q1
public void push(int x) {
while (q1.size() > 0) {
q2.add(q1.poll());
}
q1.add(x);
while (q2.size() > 0) {
q1.add(q2.poll());
}
}

public int pop() {
return q1.poll();
}

public int top() {
return q1.peek();
}

public boolean empty() {
return q1.isEmpty();
}
}

使用两个 Deque 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class MyStack {
// Deque 接口继承了 Queue 接口
// 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
Deque<Integer> que1; // 和栈中保持一样元素的队列
Deque<Integer> que2; // 辅助队列
/** Initialize your data structure here. */
public MyStack() {
que1 = new ArrayDeque<>();
que2 = new ArrayDeque<>();
}

/** Push element x onto stack. */
public void push(int x) {
que1.addLast(x);
}

/** Removes the element on top of the stack and returns that element. */
public int pop() {
int size = que1.size();
size--;
// 将 que1 导入 que2 ,但留下最后一个值
while (size-- > 0) {
que2.addLast(que1.peekFirst());
que1.pollFirst();
}

int res = que1.pollFirst();
// 将 que2 对象的引用赋给了 que1 ,此时 que1,que2 指向同一个队列
que1 = que2;
// 如果直接操作 que2,que1 也会受到影响,所以为 que2 分配一个新的空间
que2 = new ArrayDeque<>();
return res;
}

/** Get the top element. */
public int top() {
return que1.peekLast();
}

/** Returns whether the stack is empty. */
public boolean empty() {
return que1.isEmpty();
}
}

优化,使用一个 Deque 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class MyStack {
// Deque 接口继承了 Queue 接口
// 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
Deque<Integer> que1;
/** Initialize your data structure here. */
public MyStack() {
que1 = new ArrayDeque<>();
}

/** Push element x onto stack. */
public void push(int x) {
que1.addLast(x);
}

/** Removes the element on top of the stack and returns that element. */
public int pop() {
int size = que1.size();
size--;
// 将 que1 导入 que2 ,但留下最后一个值
while (size-- > 0) {
que1.addLast(que1.peekFirst());
que1.pollFirst();
}

int res = que1.pollFirst();
return res;
}

/** Get the top element. */
public int top() {
return que1.peekLast();
}

/** Returns whether the stack is empty. */
public boolean empty() {
return que1.isEmpty();
}
}

优化,使用一个 Queue 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MyStack {

Queue<Integer> queue;

public MyStack() {
queue = new LinkedList<>();
}

//每 offer 一个数(A)进来,都重新排列,把这个数(A)放到队列的队首
public void push(int x) {
queue.offer(x);
int size = queue.size();
//移动除了 A 的其它数
while (size-- > 1)
queue.offer(queue.poll());
}

public int pop() {
return queue.poll();
}

public int top() {
return queue.peek();
}

public boolean empty() {
return queue.isEmpty();
}
}

优化,使用一个 Queue 实现,但用卡哥的逻辑实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyStack {
Queue<Integer> queue;

public MyStack() {
queue = new LinkedList<>();
}

public void push(int x) {
queue.add(x);
}

public int pop() {
rePosition();
return queue.poll();
}

public int top() {
rePosition();
int result = queue.poll();
queue.add(result);
return result;
}

public boolean empty() {
return queue.isEmpty();
}

public void rePosition(){
int size = queue.size();
size--;
while(size-->0)
queue.add(queue.poll());
}
}

09、第五章 栈与队列part01
http://yuanql.top/2023/07/21/02_1_代码随想录算法训练营18期/09、第五章 栈与队列part01/
作者
Qingli Yuan
发布于
2023年7月21日
许可协议