【左程云算法08】栈和队列相互实现
目录
用栈实现队列(Leecode232)
代码演示
用队列实现栈(Leecode225)
代码演示
视频链接:算法讲解014【入门】队列和栈入门题目-栈和队列相互实现
用栈实现队列(Leecode232)
leecode232
如何解决?
--用两个栈
一个in栈,一个out栈
前提:out空了,才能倒
演示过程:加入abcde用户朝你要数了,把它倒out里面
用户朝你要a,你把a倒出,要b,你把b倒出。
用户又加数了,f和g又倒进去了。这时候不能往里倒,因为out还没空。
用户让你弹出c
如果要倒数据,in必须要倒完。
时间复杂度是O(1),因为每个数都是最多进in1次,出in一次,进out一次,出out一次。一共4次。
代码演示
/*** Stack 工具箱里的常用工具(方法)详解* 让我们用“一叠盘子”的比喻来详细解释一下每个工具的作用:* 1. push(元素) - 放盘子* 作用:把一个新元素放到栈的最顶端。* 生活中的比喻:你洗好一个新盘子,然后把它放到这叠盘子的最上面。* 代码示例:in.push(10);* 解释:把数字 10 这个“盘子”放到 in 这叠盘子的最顶端。* 2. pop() - 拿走盘子* 作用:移除栈顶的元素,并把这个元素返回给你。这是一个会改变栈结构的操作。* 生活中的比喻:你从这叠盘子的最上面拿走一个盘子。拿走之后,这叠盘子就少了一个。* 代码示例:int topElement = in.pop();* 解释:从 in 这叠盘子的顶部拿走最上面的那个盘子(比如是 10),然后把 10 这个数值存到 topElement 变量里。现在 in 栈里就没有 10 了。* 3. peek() - 看一眼盘子* 作用:查看栈顶的元素是什么,但不移除它。这个操作不会改变栈的结构。* 生活中的比喻:你只是低头看一眼最上面的盘子是什么花色,但你并不把它拿走。* 代码示例:int topElement = in.peek();* 解释:只是看一眼 in 栈顶部的元素是什么,然后把它的值存到 topElement 变量里。那个元素还在栈顶,没有被拿走。* 4. empty() - 盘子还有吗?* 作用:检查这个栈是不是空的,里面一个元素都没有。* 生活中的比喻:检查一下放盘子的地方是不是已经空了,一个盘子都没有了。* 代码示例:if (in.empty()) { ... }* 解释:判断 in 栈是否为空。如果为空,它会返回 true(是);如果不为空,它会返回 false(不是)。* 小提示:在更新的 Java 版本中,更推荐使用 isEmpty() 方法,它们的作用是一样的,但 isEmpty() 是更现代的命名习惯。不过 empty() 仍然完全可用。*/import java.util.Stack;//导入java内置的stack类,我们需要用它来创建栈对象
//这个类模拟了一个队列,但是它是用两个栈实现的
public class MyQueue {//“in”栈:主要用于接收新进来的元素(push操作)//stack<Integer>表示这是一个只能存放正数的栈。//意思就是:“好了,现在请给我一个新的、空的、专门放盘子(整数 Integer)的 Stack 工具箱,我给它取个名字叫 in。”public Stack<Integer> in;//“out”栈:主要用于提供队头元素(pop和peek操作)public Stack<Integer> out;//构造方法:当创建一个MyQueue对象时,这个方法会被自动调用public MyQueue(){//初始化“in”栈和“out”栈in = new Stack<Integer>();//创建一个空的整数栈给“in”out = new Stack<Integer>();//创建一个空的整数栈给“out”}//负责将“in”栈中的数据倒腾到“out”中private void inToOut(){//倒数据 从in栈倒入out//重点:out空了才能倒数据//重点:如果倒数据,in必须倒完!//检查out栈是否为空,只有是空时,我们才进行倒。if(out.empty()){//如果out栈为空,我们就把所有元素倒入out栈中//循环条件:只要in不为空,就继续倒腾while(!in.empty()){// 将 'in' 栈顶的元素 'pop' 出来(移除并返回)// 然后立即 'push' 到 'out' 栈的顶部。// 这样操作会使得原来 'in' 栈中最底部的元素(最早进入的)最终位于 'out' 栈的顶部,// 从而实现了“先进先出”的准备。out.push(in.pop());}}}
/*** 从队列中移除并返回队头元素(出队操作)*/
public int pop(){// 在尝试从 'out' 栈 'pop' 元素之前,先调用 'inToOut()'。// 这确保了如果 'out' 栈为空(或者通过 push 之后 out 栈不为空但 in 栈有新元素),// 那么 'in' 栈中的元素会被正确地转移和排序到 'out' 栈中。inToOut();// 此时,'out' 栈的栈顶元素就是队列的队头元素(最老的元素)。// 移除并返回它。return out.pop();
}
/*** 查看队列的头元素,但不移除它*/
public int peek() {// 和 pop() 类似,先调用 'inToOut()' 确保 'out' 栈准备就绪。inToOut();// 返回 'out' 栈的栈顶元素,但不移除它。return out.peek();}/*** 判断队列是否为空。** @return 如果队列为空,则返回 true;否则返回 false。*/public boolean empty() {// 只有当 'in' 栈和 'out' 栈都为空时,整个队列才算是空的。// 因为如果 'in' 栈有元素,即使 'out' 栈空了,这些元素也仍在队列中等待处理。return in.empty() && out.empty();}
}
用队列实现栈(Leecode225)
leecode225
给你一个队列,只能从尾部进,从头部出。
我们先放入a
再放入b,我们注意到,b放进来之前队列里面有1个数。我们进行以下的操作:我们让a从头部弹出,再让a从尾部进入。这个行为做1次。是不是就是栈了?
再来,要加c。加c之前,观察到队列里面有2个数。所以之前讲的那个行为我们要做两次。
第一次,b从头出来从尾进;第二次,a从头出来从尾进。所以这样就实现栈了。
以此类推,d进来也是一样的。
代码演示
import java.util.LinkedList;//导入LinkList类
import java.util.Queue;//导入Queue接口
public class MyStack {//我们使用一个Queue来作为底层的数据存储//Queue<Integer>表示这是一个只能存放整数的队列Queue<Integer> queue;//构造方法:当创建一个MyStack对象时,这个方法会被调用public MyStack(){// 这里有一个对 Java 新手很重要的知识点:// Queue 是一个“接口”(Interface),你可以把它理解为一个“规范”或“蓝图”,它只定义了队列应该有哪些功能(比如 offer, poll)。// LinkedList 是一个“具体实现类”(Class),它是真正能干活的类,并且它遵守了 Queue 的规范。// 所以我们 new 一个 LinkedList,然后用一个 Queue 类型的变量来引用它。queue = new LinkedList<Integer>();}/*** 将一个元素推入栈顶(入栈操作)* 这是整个实现最核心,最复杂的部分* O(n)表示这个操作的时间复杂度是n,一位着如果栈里有n个元素,这个操作大概需要n步*/public void push(int x) {//第一步:记录下在添加新元素之前,队列里已经有多少个“老元素”int n = queue.size();//第二步:像正常的队列一样,把新元素x添加到队尾queue.offer(x);//offer是队列的入队方法//第三步:乾坤大挪移//我们要循环n次,把排在x前面的n个老元素,全部挪到x后面去for (int i = 0; i < n; i++) {//1.queue.poll()从队头取出一个元素(最老的元素// 2.queue.offer()立刻把刚刚取出的哪个元素添加到队尾//循环n次后就可以了queue.offer(queue.poll());}}/*** 移除并返回栈顶的元素(出栈操作)*/public int pop(){//因为我们的push操作已经保证了最新的元素永远在队头//所以我们只需要执行队列的出队操作就可以//poll方法会移除并返回队头的元素return queue.poll();}/*** 查看栈顶的元素,但不移除它。** @return 栈顶的元素。*/public int top() {// 同样,因为栈顶元素就在队头,// 我们只需要用队列的 peek() 方法查看一下队头元素是什么就可以了。// peek() 方法只查看,不移除。return queue.peek();}/*** 判断栈是否为空。** @return 如果栈为空,则返回 true;否则返回 false。*/public boolean empty() {// 直接判断底层的队列是否为空即可。return queue.isEmpty();}}