Carl代码随想录算法训练营-Day 10-栈与队列理论基础、232.用栈实现队列、225. 用队列实现栈

摘要

从原理上分析了栈与队列的理论基础知识,并用自定义双链表结构完成了实现。完成了leetcode上232.用栈实现队列、225. 用队列实现栈,一共两道相关题目,进行了具体的思路分析以及Java代码实现。

栈与队列理论基础

原理简析

栈和队列,是计算机基本的数据结构。

栈最为人熟知的就是它后进先出LIFO(Last In First Out)的特性。栈就像一个木桶,不管什么时候,我们往其中放入木板(压栈),新放入的木板都在上面(栈顶)。而我们要取出木板(出栈),第一个取出的都是最后放进木桶的木板(后进先出),随后才能取出下面的木板。
请添加图片描述

队列

队列与栈相反,其特性是先进先出FIFO(First In First Out)。队列就是一根隧道,火车进入隧道(进队列),火车头先进入隧道一端(队首),火车尾随后进入;火车出隧道时(出队列),火车头先从隧道另一段出来(队尾),火车尾随后出来。
在这里插入图片描述
通过以上两个比喻的描述,我们可以很清晰地认识到这两个数据结构的原理。那么如何用代码来实现它们呢?

代码实现

本文使用leetcode《707.设计链表》中的自定义双链表MyLinkedList实现栈。
博主的《Carl代码随想录算法训练营-Day 3- 链表理论基础、203.移除链表元素、707.设计链表、24.两两交换链表中的节点》中有该自定义双链表的详细Java实现代码。
但此处新添加两个方法:
size():返回一个int,表示链表长度。
isEmpty:返回一个boolean,表示链表是否为空。

//MyLinkedList的类结构
package Leetcode.editor.util;
public class MyLinkedList{
    final private ListNode _head;
    final private ListNode _tail;
    private int _size;
    private class ListNode{
    	int val;
        ListNode next,prev;
    }
        public ListNode(int val,ListNode next,ListNode prev){}
        public ListNode(){}
    }
    public MyLinkedList() {}
    private ListNode get(int index,boolean mark){}
    public int get(int index) {}
    public void addAtHead(int val) {}
    public void addAtTail(int val) {}
    public void addAtIndex(int index, int val) {}
    public void deleteAtIndex(int index) {}
    public int size(){}
    public int isEmpty(){}
}

栈的实现

对于栈,我们可以使用一个链表(或数组)来实现。
压栈(push)操作:新建节点接在链表尾部。
出栈(pop)操作:将链表尾节点取出并删除。
栈顶(peek)操作:查询链表尾节点的值。
查看栈内元素个数(size):返回链表节点个数。
栈是否为空(isEmpty):返回链表长度是否为0。

package leetcode.editor.util;
import java.util.NoSuchElementException;
public class MyStack {
    private final MyLinkedList list;
    public MyStack(){this.list=new MyLinkedList<>();}
    public void push(int val){list.addAtTail(val);}
    public int pop(){
        if(list.isEmpty()){throw new NoSuchElementException();}
        int last = list.size() - 1;
        int val = list.get(last);
        list.deleteAtIndex(last);
        return val;
    }
    public int peek(){
        if(list.isEmpty()){throw new NoSuchElementException();}
        int last = list.size() - 1;
        return list.get(last);
    }
    public int size(){return list.size();}
    public boolean isEmpty(){return list.isEmpty();}
}

队列的实现

对于队列,我们同样可以使用一个链表(或数组)来实现。
进队(offer)操作:新建节点接在尾部。
出队(poll)操作:将链表头节点取出并删除。
队顶(peek)操作:查询链表头节点的值。
查看队列内元素个数(size):返回链表节点个数。
队列是否为空(isEmpty):返回链表长度是否为0。

package leetcode.editor.util;

import java.util.NoSuchElementException;

public class MyQueue {
    private final MyLinkedList<Integer> list;
    public MyQueue(){this.list=new MyLinkedList<>();}
    public void offer(int val){list.addAtTail(val);}
    public int poll(){
        if(list.isEmpty()){throw new NoSuchElementException();}
        int val = list.get(0);
        list.deleteAtIndex(0);
        return val;
    }
    public int peek(){
        if(list.isEmpty()){throw new NoSuchElementException();}
        return list.get(0);
    }
    public int size(){return list.size();}
    public boolean isEmpty(){return list.isEmpty();}
}

232、用栈实现队列

LeetCode题目链接

思路分析

我们知道栈只有一个口,同时用于进出,而队列有两个口,一个进一个出;而且栈的元素进出顺序相反,队列的元素,进出顺序是相同的。
因此要用栈实现队列,我们就需要两个栈,一个为进栈stackIn,一个为出栈stackOut,这样经过反-反为正的处理之后,元素进出顺序就一致了。
进队(offer)操作,调用stackIn.push()进行压栈即可。
出队(poll),先看stackOut是否为空,如果是,就将stackIn中的全部元素依次弹出,并压入stackOut中。最后再弹出stackOut的栈顶元素即可。
返回栈顶(peek),先出队,然后再将该元素压回stackOut

代码实现

class MyQueue {
        private final Stack<Integer> stackIn;
        private final Stack<Integer> stackOut;
        public MyQueue() {
            stackIn = new Stack<>();
            stackOut = new Stack<>();
        }
        public void push(int x) {
            stackIn.push(x);
        }
        public int pop() {
            if (stackOut.isEmpty()) {
                while (!stackIn.isEmpty()) {
                    stackOut.push(stackIn.pop());
                }
            }
            return stackOut.pop();
        }
        public int peek() {
            int x = this.pop();
            stackOut.push(x);
            return x;
        }
        public boolean empty() {
            return stackIn.isEmpty() && stackOut.isEmpty();
        }
    }

225、用队列实现栈

LeetCode题目链接

思路分析

如果用双端队列实现栈,这将非常简单,与用双链表实现栈是一摸一样。进栈直接加在队列尾部,出栈直接删掉队列尾部。
如果用单端队列实现栈,将复杂一些。我们主要分析单端队列实现栈的进出操作。
进栈(push),同样使用队列的offer操作即可。
出栈(pop),我们发现必须要将队列尾部元素弹出才行。但对于单端队列来说,尾部元素一定是最后出队的。故而,我们可以让队列先弹出尾部元素前面所有元素,并将它们重新加入队列中。完成这个操作之后,队列头部就是我们要出栈的元素了,可以直接出队列。

代码实现

class MyStack {
        private final LinkedList<Integer> list;
        public MyStack() {
            this.list = new LinkedList<>();
        }
        public void push(int x) {
            list.offer(x);
        }
        public int pop() {
            for (int i = 0; i < list.size() - 1; i++) {
                list.offer(list.poll());
            }
            return list.poll();
        }
        public int top() {
            int x = this.pop();
            list.offer(x);
            return x;
        }
        public boolean empty() {
            return list.isEmpty();
        }
    }

总结和思考

今天我们通过分析栈和队列的基础原理,分别使用自定义双链表实现了栈和队列,又分别用队列实现了栈,用栈实现了队列,这对于我们学习栈和队列的数据结构相关知识起到了重要作用。
希望对大家的算法学习有所帮助,谢谢阅读!

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值