剑指 Offer 09. 用两个栈实现队列

137 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

Leetcode : leetcode-cn.com/problems/yo…

GitHub : gitee.com/nateshao/le…

剑指 Offer 09. 用两个栈实现队列

题目描述:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1

示例 1 输入:

["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]

示例 2 输入:

["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

提示:1 <= values <= 10000。 最多会对 appendTaildeleteHead 进行 10000 次调用


思路和算法:初始化两个栈,第一个栈支持插入操作,第二个栈支持删除操作。

根据栈先进后出的特性,我们每次往第一个栈里插入元素后,第一个栈的底部元素是最后插入的元素,第一个栈的顶部元素是下一个待删除的元素。为了维护队列先进先出的特性,我们引入第二个栈,用第二个栈维护待删除的元素,在执行删除操作的时候我们首先看下第二个栈是否为空。如果为空,我们将第一个栈里的元素一个个弹出插入到第二个栈里,这样第二个栈里元素的顺序就是待删除的元素的顺序,要执行删除操作的时候我们直接弹出第二个栈的元素返回即可。

成员变量

  • 维护两个栈 stack1stack2,其中 stack1 支持插入操作,stack2 支持删除操作

构造方法

  • 初始化 stack1stack2 为空

插入元素

插入元素对应方法 appendTail

  • stack1 直接插入元素

删除元素

删除元素对应方法 deleteHead

  • 如果 stack2 为空,则将 stack1 里的所有元素弹出插入到 stack2 里
  • 如果 stack2 仍为空,则返回 -1,否则从 stack2 弹出一个元素并返回
package com.nateshao.sword_offer2.Code_09_CQueue;


import java.util.LinkedList;

/**
 * @date Created by 邵桐杰 on 2022/3/5 22:04
 * @微信公众号 千羽的编程时光
 * @个人网站 www.nateshao.cn
 * @博客 https://nateshao.gitee.io
 * @GitHub https://github.com/nateshao
 * @Gitee https://gitee.com/nateshao
 * Description:
 * <p>
 * 剑指 Offer 09. 用两个栈实现队列
 * 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead .
 * 分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
 * <p>
 * 示例 1:
 * <p>
 * 输入:
 * ["CQueue","appendTail","deleteHead","deleteHead"]
 * [[],[3],[],[]]
 * 输出:[null,null,3,-1]
 * 示例 2:
 * <p>
 * 输入:
 * ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
 * 输出:[null,-1,null,null,5,2]
 * [[],[],[5],[2],[],[]]
 */
public class CQueue {

    /**
     * 思路:
     * 加入队尾 appendTail()函数: 将数字 val 加入栈 A 即可。
     * 删除队首deleteHead()函数: 有以下三种情况。
     * 当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
     * 否则,当 A 为空: 即两个栈都为空,无元素,因此返回 −1 。
     * 否则: 将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。
     */
    /**
     * 初始化两个链表,
     * // 栈 B 元素实现栈 A 元素倒序 ,然后通过B.removeLast()将元素移出去
     * // 栈A作为辅助元素
     */
    LinkedList<Integer> A, B;// 栈A支持插入操作,栈B支持删除操作


    public CQueue() {
        A = new LinkedList<Integer>();
        B = new LinkedList<Integer>();
    }

    /**
     * 添加
     *
     * @param value
     */
    public void appendTail(int value) {
        A.addLast(value);
    }

    /**
     * 删除
     *
     * @return
     */
    public int delete() {
        // 当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
        if (!B.isEmpty()) return B.removeLast();
        // 否则,当 A 为空: 即两个栈都为空,无元素,因此返回 −1 。
        if (A.isEmpty()) return -1;
        // 否则: 将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。
        while (!A.isEmpty()) B.addLast(B.removeLast());
        return B.removeLast();
    }

}

注意:两个原则:

  1. 栈push倒出栈pop时,要一次性倒完,不能倒2个;
  2. 栈pop有数据时,栈push不能导入数据。否则会乱。

Go

package main

import "container/list"

type CQueue struct {
	stack1, stack2 *list.List
}

func Constructor() CQueue {
	return CQueue{
		stack1: list.New(),
		stack2: list.New(),
	}
}

func (this *CQueue) AppendTail(value int)  {
	this.stack1.PushBack(value)
}

func (this *CQueue) DeleteHead() int {
	if this.stack2.Len() == 0 {
		for this.stack1.Len() > 0 {
			this.stack2.PushBack(this.stack1.Remove(this.stack1.Back()))
		}
	}
	if this.stack2.Len() != 0 {
		e := this.stack2.Back()
		this.stack2.Remove(e)
		return e.Value.(int)
	}
	return -1
}

自己写是这样的:

    /**
     * 两个栈实现队列
     *
     * 思路:
     * 1. 初始化两个栈。栈push只进,栈pop只出。
     * 2. 比如:12345存入栈push,然后倒序(要一次倒完)存入栈pop
     * 3. 在将5输出。
     * 4. 两个原则:(1)栈push倒出栈pop时,要一次性倒完,不能倒2个;
     *             (2)栈pop有数据时,栈push不能导入数据。否则会乱
     */
    public static class TwoStacksQueue {
        private Stack<Integer> stackPush;
        private Stack<Integer> stackPop;

        public TwoStacksQueue() {
            stackPush = new Stack<Integer>();
            stackPop = new Stack<Integer>();
        }

        public void push(int pushInt) {
            stackPush.push(pushInt);
        }

        public int poll() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.pop();
        }

        public int peek() {
            if (stackPop.empty() && stackPush.empty()) {
                throw new RuntimeException("Queue is empty!");
            } else if (stackPop.empty()) {
                while (!stackPush.empty()) {
                    stackPop.push(stackPush.pop());
                }
            }
            return stackPop.peek();
        }
    }

举一反三:图的优先遍历 (用栈实现)

如果仅用栈实现,如何实现图的深度优先遍历?

解决:用两个队列结构,拼出一个栈结构,再用栈结构实现图的有限遍历

参考链接:leetcode-cn.com/problems/yo…