代码随想录--栈与队列--用队列实现栈

题目

使用队列实现栈的下列操作:

push(x) -- 元素 x 入栈
pop() -- 移除栈顶元素
top() -- 获取栈顶元素
empty() -- 返回栈是否为空

注意:

你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

思路

(这里要强调是单向队列)

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

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

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

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

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

模拟的队列执行语句如下:
queue.push(1);
queue.push(2);
queue.pop(); // 注意弹出的操作
queue.push(3);
queue.push(4);
queue.pop(); // 注意弹出的操作
queue.pop();
queue.pop();
queue.empty();
在这里插入图片描述详细如代码注释所示:
class MyStack {
public:
queue que1;
queue que2; // 辅助队列,用来备份

/** Initialize your data structure here. */
MyStack() {

}

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

/** Removes the element on top of the stack and returns that element. */
int pop() {
    int size = que1.size();
    size--;
    while (size--) { // 将que1 导入que2,但要留下最后一个元素
        que2.push(que1.front());
        que1.pop();
    }

    int result = que1.front(); // 留下的最后一个元素就是要返回的值
    que1.pop();
    que1 = que2;            // 再将que2赋值给que1
    while (!que2.empty()) { // 清空que2
        que2.pop();
    }
    return result;
}

/** Get the top element.
 ** Can not use back() direactly.
 */
int top(){
    int size = que1.size();
    size--;
    while (size--){
        // 将que1 导入que2,但要留下最后一个元素
        que2.push(que1.front());
        que1.pop();
    }

    int result = que1.front(); // 留下的最后一个元素就是要回返的值
    que2.push(que1.front());   // 获取值后将最后一个元素也加入que2中,保持原本的结构不变
    que1.pop();

    que1 = que2; // 再将que2赋值给que1
    while (!que2.empty()){
        // 清空que2
        que2.pop();
    }
    return result;
}

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

};

时间复杂度: pop为O(n),top为O(n),其他为O(1)
空间复杂度: O(n)

优化

其实这道题目就是用一个队列就够了。

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

C++优化代码
class MyStack {
public:
queue que;

MyStack() {

}

void push(int x) {
    que.push(x);
}

int pop() {
    int size = que.size();
    size--;
    while (size--) { // 将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部
        que.push(que.front());
        que.pop();
    }
    int result = que.front(); // 此时弹出的元素顺序就是栈的顺序了
    que.pop();
    return result;
}

int top(){
    int size = que.size();
    size--;
    while (size--){
        // 将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部
        que.push(que.front());
        que.pop();
    }
    int result = que.front(); // 此时获得的元素就是栈顶的元素了
    que.push(que.front());    // 将获取完的元素也重新添加到队列尾部,保证数据结构没有变化
    que.pop();
    return result;
}

bool empty() {
    return que.empty();
}

};

时间复杂度: pop为O(n),top为O(n),其他为O(1)
空间复杂度: O(n)

其他语言版本

Java:

使用两个 Queue 实现方法1

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

class MyStack {
//q1作为主要的队列,其元素排列顺序和出栈顺序相同
Queue q1 = new ArrayDeque<>();
//q2仅作为临时放置
Queue 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 实现

class MyStack {
// Deque 接口继承了 Queue 接口
// 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
Deque que1; // 和栈中保持一样元素的队列
Deque 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 实现

class MyStack {
// Deque 接口继承了 Queue 接口
// 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
Deque 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 实现

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 实现,但用卡哥的逻辑实现

class MyStack {
Queue 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());
}

}

### 回答1: 《代码随想录知识星球精华-大厂面试八股文v1.1.pdf》是一份介绍八股文的面试指南,针对大厂面试常见题型进行分类,以及给出相应的解答思路。这份资料具有很高的参考价值,在为即将面试的求职者提供指引的同时,也可以让读者对八股文的思维框架和策略有更好的了解。 这份资料主要包括面试题目的主题分类,如动态规划、字符串、树等,每个分类下又相应给出了典型的问题,如“最长公共子序列”、“二叉树的层次遍历”等等。对于每个问题,都给出了具体的思路指导和代码模板,许多题目给出了多种不同的解法,这些解法包括时间复杂度和空间复杂度的优化。 这份资料对于求职者来说,意义重大。在面试中,对于某些问题我们可能没有完全解决,但如果有了这份资料,我们在面试中也会有一个清晰的思考框架和即时可用的代码模板。此外,这份资料同样适用于对算法和数据结构感兴趣的开发者和工程师,对于自学和提高都有帮助。 总之,《代码随想录知识星球精华-大厂面试八股文v1.1.pdf》是一个非常实用的参考材料,它是广大求职者和程序员不可或缺的工具,值得一读。 ### 回答2: 《代码随想录知识星球精华-大厂面试八股文v1.1.pdf》是一份由知名博主“代码随想”的知识星球推出的热门资料。该资料主要介绍了大厂面试中常见的八股文,包括但不限于动态规划、双指针、贪心算法、字符串操作等。 通过学习该资料,可以系统地学习和掌握各种常见的算法和数据结构,有助于提高自己的代码能力和面试竞争力。此外,资料还给出了一些实际的面试题目例子,对于准备去大厂面试的人来说,是一份非常实用的资料。 当然,要想真正掌握这些算法和数据结构,需要自己的不断练习和实践。只有在解决实际问题的过程中,才能真正体会到这些算法和数据结构的作用和优越性。 总之,该资料对于想要深入了解算法和数据结构、提高面试竞争力的人来说是一份值得推荐的优秀资料。 ### 回答3: 代码随想录知识星球精华-大厂面试八股文v1.1.pdf是一份关于大厂面试八股文的精华资料,它收集整理了各个大厂面试中常见的八股文题目和解题思路,对于准备求职或者升职的程序员来说是一份非常有价值的资料。 该资料中涵盖了常见的算法、数据结构、操作系统、计算机网络、数据库、设计模式等知识点,每个知识点都有详尽的讲解和相应的面试题目,可以帮助面试者全面了解每个知识点的考察方向和难点。 此外,该资料还提供了八股文的解题思路和解题方法,强调了实战经验和面试技巧,对于提高面试的成功率也是非常有帮助的。 总之,代码随想录知识星球精华-大厂面试八股文v1.1.pdf是一份非常实用的面试资料,它帮助面试者深入了解各个知识点的考察方向和难点,提高了应对面试的能力和成功率,对于准备求职或者升职的程序员来说是一份不可或缺的资料。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值