浅谈Java集合框架之栈和队列的使用及实现

1.队列(Queue)

1.1 概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 的特性。
入队列:进行插入操作的一端称为队尾(Tail/Rear)
出队列:进行删除操作的一端称为队头(Head/Front)

图示:
在这里插入图片描述

1.2 仿写Queue接口

可以参考Jjava api文档,可以发现:

  • Queue继承自Collection接口,由LinkedList实现

  • 一组方法通过特殊返回值通知调用者发生错误:

    • boolean:offer(E e):插入
    • E peek( ):查看
    • E poll( ):删除
  • 一组方法通过异常通知调用者方式错误:

    • boolean add(E e):插入
    • E element( ):查看
    • E remove( ):删除

仿写真正的Queue接口,元素类型,固定成Integer类型,不用泛型
示例代码:

public interface Queue {
    /**
     * 通过抛出异常,通知错误
     */

    /**
     *
     * 永远返回true
     */
   default boolean add(Integer e){
       if(offer(e)==false){
           throw new IllegalStateException();
       }
       return true;
   }

    /**
     * 查看队首元素
     */

    default Integer element(){
        Integer e = peek();
        if(e==null){
            throw new NoSuchElementException();
        }
        return e;
    }
    /**
     * 返回并删除元素
     */
    default Integer remove(){
        Integer e = poll();
        if(e==null){
            throw new NoSuchElementException();
        }
        return e;
    }


    /**
     * 通过特殊的返回值,通知错误
     */
    /**
     * 插入
     * true成功,false 失败
     */
    boolean offer(Integer e);

    /**
     * 查看队首元素
     */

    Integer peek();

    /**
     * 返回并删除元素
     */
    Integer poll();

}

1.3 循环队列的实现

1.3.1 不开辟额外空间

通过引入size来区分队列是否为空or满,不用开辟额外空间,即k为数组length

图示:
插入元素:
在这里插入图片描述
删除元素:
在这里插入图片描述

方法说明:

  • 对于enQueue:如果队列不满,rear和size 进行++,最如果rear等于数组length,则将rear置为0
  • 对于deQueue:如果队列不为空,size进行 --, front 进行++,最后如果front==数组length,则将front置为0
  • 对于Front:不为空,返回 array[front]
  • 对于Rear:不为空,如果rear==0了,则返回 array[length-1],否则返回array[rear-1]
  • 对于isEmpty:size为0 则空
  • 对于IsFull:size==数组length则满

代码示例:

public class MyCircularQueue {

    private int[] array;
    private int size;
    private int front;
    private int rear;
    public MyCircularQueue(int k) {
        array = new int[k];
        size=0;
        front = 0;
        rear = 0;
    }

    public boolean enQueue(int value) {
        if(isFull()) {
            return false;
        }
        array[rear++] = value;
        size++;
        if(rear==array.length) {
            rear = 0;
        }
        return true;
    }

    public boolean deQueue() {
        if(isEmpty()) {
            return false;
        }
        size--;
        front++;
        if(front==array.length) {
            front = 0;
        }
        return true;
    }

    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        return array[front];
    }

    public int Rear() {
        if(isEmpty()) {
            return -1;
        }
        if(rear == 0) {
            return array[array.length-1];
        }
        return array[rear-1];
    }

    public boolean isEmpty() {

        return size==0;
    }

    public boolean isFull() {
        return size==array.length;
    }
}
1.3.2 需要开辟一个额外空间

直接使用front 和 rear 判空和判满,需要多开辟一个空间,即k+1为数组length

图示:
插入元素:
在这里插入图片描述
删除元素:
在这里插入图片描述

方法说明:

  • 对于enQueue:如果rear==k+1,则将rear置为0
  • 对于deQueue:如果front==k+1,则将front置为0
  • 对于Front:不为空,返回array[front]
  • 对于Rear:不为空,且rear为0,返回array[k],否则返回array[rear-1]
  • 对于IsEmpty:当front等于rear为空
  • 对于IsFull:当 rear+1 % k+1 == front 则为满

代码示例:

public class MyCircularQueue {
    private int[] array;
    private int rear,front;
    private int k;
    public MyCircularQueue_self(int k) {
        this.k = k;
        array = new int[k+1];
        front = rear = 0;
    }

    /**
     * k+1 就是 数组length
     * @param value
     * @return
     */
    public boolean enQueue(int value) {
        if(isFull()){
            // System.out.println("sasd");
            return false;
        }
        array[rear++] = value;
        if(rear==k+1){
            rear = 0;
        }
        return true;
    }

    public boolean deQueue() {
        if(isEmpty()){
            return false;
        }
        front++;
        if(front==k+1){
            front = 0;
        }

        return true;
    }

    public int Front() {
        if(isEmpty()){
            return -1;
        }
        return array[front];
    }

    public int Rear() {
        if(isEmpty()){
            return -1;
        }
        if(rear==0){
            return array[k];
        }
        return array[rear-1];
    }

    public boolean isEmpty() {
        return front==rear;
    }

    public boolean isFull() {
       return (rear+1)%(k+1)==front;
    }

2. 双端队列 (Deque)

2.1 概念

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

2.2 仿写Deque接口

通过Java api文档可以发现,Deque继承自Queue

方法说明:

  • 一组方法通过特殊值报错:

    • boolean offerFirst/offerLast(E e):头插,尾插
    • E peekFirst( )/peekLast( ):查看队首/队尾元素
    • E pollFirst( )/pollLast( ):头删,尾删
  • 一组方法通过异常通知调用者发生错误:

    • void addFirst/addLast(E e):头插/尾插
    • E removeFirst/removeLast( ):头删/尾删,返回并删除元素
    • E getFirst/getLast( ):查看队首/队尾元素

进行仿写:

public interface Deque extends Queue {
    //通过特殊值报错

    boolean offerFirst(Integer e);
    Integer peekFirst();
    Integer pollFirst();

    boolean offerLast(Integer e);
    Integer peekLast();
    Integer pollLast();

    //通过抛出异常报错

    default void addFirst(Integer e){
        if(offerFirst(e)==false){
            throw new IllegalStateException();
        }

    }

    default Integer getFirst(){
        Integer e = peekFirst();
        if(e==null){
            throw new NoSuchElementException();
        }
        return e;
    }

    default Integer removeFirst(){
        Integer e = pollFirst();
        if(e==null){
            throw new NoSuchElementException();
        }
        return e;

    }


    default void addLast(Integer e){
        if(offerLast(e)==false){
            throw new IllegalStateException();
        }

    }

    default Integer getLast(){
        Integer e = peekLast();
        if(e==null){
            throw new NoSuchElementException();
        }
        return e;
    }

    default Integer removeLast(){
        Integer e = pollLast();
        if(e==null){
            throw new NoSuchElementException();
        }
        return e;

    }


    //这组方法继承于Queue

    default boolean offer(Integer e){
        return offerLast(e);
    }
    default Integer peek(){
        return peekFirst();
    }

    default Integer poll(){
        return pollFirst();
    }


    //下面这组方法为栈的形态准备

    default void push(Integer e){
        addFirst(e);
    }

    default Integer pop(){
        return removeFirst();
    }
}

3. 栈 (Stack)

3.1 概念

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。

  • 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
  • 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
  • 出栈:栈的删除操作叫做出栈。出数据在栈顶

图示:
在这里插入图片描述

3.2 栈的实现

可以用数组来实现,但是链表效率更高一些。通过Java api 文档发现,LinkedList 实现了Deque接口,Deque又继承了Queue接口

对LinkedList的仿写:

public class LinkedList implements Deque{
    private static class Node{
        private Integer v;
        Node prev;
        Node next;

        Node(Integer x){
            v = x;
        }
    }
    private Node head;
    private Node tail;
    private int size;

    /**
     * 头插
     * @param e
     * @return
     */
    @Override
    public boolean offerFirst(Integer e) {
        Node node = new Node(e);
        if(size==0){
            head = tail = node;
        }else{
            node.next = head;
            head.prev = node;
            head = node;
        }

        return true;
    }

    /**
     * 查看头部元素
     * @return
     */
    @Override
    public Integer peekFirst() {
        if(size==0){
            return null;
        }
        return head.v;
    }

    /**
     * 头删除
     * @return
     */
    @Override
    public Integer pollFirst() {
        if(size==0){
            return null;
        }
        Integer e = head.v;
        head = head.next;
        if(head!=null){
            head.prev=null;
        }else{
            tail = null;
        }
        return e;
    }

    /**
     * 尾插
     * @param e
     * @return
     */
    @Override
    public boolean offerLast(Integer e) {
        Node node = new Node(e);
        return false;
    }

    /**
     * 查看尾部元素
     * @return
     */
    @Override
    public Integer peekLast() {
        if(size==0){
            return null;
        }
        return tail.v;
    }

    /**
     * 尾删
     * @return
     */
    @Override
    public Integer pollLast() {
        if(size==0){
            return null;
        }
        Integer e = tail.v;

        tail = tail.prev;
        if(tail!=null){
            tail.next=null;

        }else{
            head = null;
        }
        size--;
        return e;
    }
}

因为LinkedList实现了Deque,而Deque又继承了Queue,所以可以发现:
作为栈使用时核心方法为:

  • E push(E item):压栈
  • E pop():出栈
  • E peek():查看栈顶元素
  • boolean isEmpety():判断栈是否为空

4.小结

java中的栈和队列实际上都是由LinkedList表现出来的,所以当:

4.1作为Stack使用
方法解释
E push(E item)压栈
E pop()出栈
E peek()查看栈顶元素
boolean isEmpety()判断栈是否为空
4.2 作为Queue使用
错误处理抛出异常返回特殊值
入队列add(e)offer(e)
出队列remove()poll()
队首元素element()peek()
4.3 作为Deque使用

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值