使用Java实现循环队列(基于数组),链表队列(基于双向链表)

1. 基于数组实现循环队列

package com.feifei.demo.queue;

import java.io.Serializable;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * 自定义循环队列
 * 1. 单队列会出现假溢出的情况,也就是队列有空闲空间,但是无法插入
 * 队列有2个指针标识位,一个为front(用于指向队首) 一个为rear(用于指向队尾)
 * 在队列为空时,front和rear相同
 * front
 * rear
 * null   null   null   null   null
 *
 * 插入一个值后, rear后移一位, 只有当队列出队时,front才会发生偏移,请记住,
 * 队列的队首一直指向队列的首部(有数据的首部)
 * front
 *        rear
 *   1    null   null   null   null
 *
 * 当队列rear达到数组长度时,此时在出队一个数据,模型如下所示:
 *          front
 *                             rear
 * null      1     2      3    null
 *
 * 此时我们在插入数据5到队尾时,就会出现假溢出情况,明明还有存储空间,但是rear已经越界了
 *          front
 *                                  rear
 * null      1     2      3     4   越界
 *
 * 此时,我们需要循环队列(逻辑上来说,实际还是数组)来完成此操作,当插入数据4时,我们的rear应该指        
 * 向数组索引下标为0的单元格,采用通用公式为 rear = (rear + 1) % capacity, 其中capacity为数 
 * 组长度此时模型如下所示:
 *          front
 * rear
 * null      1     2      3     4   
 *
 * rear到达数组长度时,这里我们标识数组长度为capacity,此时我们再次插入数据,此时队列已满
 *          front
 *          rear
 *  5        1       2        3       4
 *
 * 但是我们发现队列为空和队列已满时,都满足front == rear,为了区分,我们通常采用的做法是在队列还 
 * 剩一个空的单元格未插入时标识队列已满,比如:
 *          front
 * rear
 * null      1     2      3     4   
 *
 * 这个就表示队列已满,满足front = (rear + 1) % capacity即标识队列已满
 *
 */
public class CircularQueue<E> implements Serializable {

    private static final long serialVersionUID = -2938153003420395845L;

    /**
     * 数据存储数组
     */
    private transient Object[] elementData;

    /**
     * 定义队首
     */
    private transient int front;

    /**
     * 定义队尾
     */
    private transient int rear;

    /**
     * 定义元素个数
     */
    private int size;

    /**
     * 自定义容量
     */
    private int capacity;

    /**
     * 定义默认的数组容量大小, 定义为11的目的是为了实现预留一个存储空间来
     * 区分队列是否已存满和队列是否为空, 因为队列为满和为空时的front == rear
     */
    private transient static final int DEFAULT_CAPACITY_SIZE = 11;

    public int size() {
        return size;
    }

    public CircularQueue() {
        elementData = new Object[(capacity = DEFAULT_CAPACITY_SIZE)];
    }

    public CircularQueue(int capacity) {
        if (capacity <= 0) {
            elementData = new Object[(this.capacity = DEFAULT_CAPACITY_SIZE)];
        } else {
            this.capacity = capacity;
            elementData = new Object[(capacity + 1)];
        }
        this.size = 0;
    }

    /**
     * 入队,数据添加至队尾
     * 校验队列是否已满,因为队列为空
     * @return
     */
    public boolean offer(E element) {
        if (Objects.isNull(element)) {
            return false;
        }
        // 校验队列是否已满, 如果队列已满则不新增
        if ((rear + 1) % capacity == front) {
            System.out.println("队列已满, 不可再新增数据");
            return false;
        }

        elementData[rear] = element;
        size++;
        // 将队尾重新赋值,这里不直接进行加一的原因在于可能会出现假溢出的情况
        // 比如说已经存在队尾的索引为4,(4 + 1) % 5 = 0 => 队尾指向了索引下标为0
        rear = (rear + 1) % capacity;

        return true;
    }

    /**
     * 出队,数据从队首中被移除
     * @return
     */
    public E poll() {
        // 校验队列是否为空,为空则不允许删除
        if (rear == front) {
            System.out.println("队列为空,不允许删除");
            return null;
        }

        E elementDatum = (E) elementData[front];

        elementData[front] = null;
        // 将队首重新赋值,赋值逻辑和队尾逻辑类似
        front = (front + 1) % capacity;
        // 将队列数据长度进行减一操作
        size--;
        return elementDatum;
    }

    public static void main(String[] args) {

        CircularQueue<Integer> queue = new CircularQueue<>();
        System.out.println("queue = " + queue.size());
        System.out.println("queue = " + queue.capacity);
        queue.offer(21);
        queue.offer(22);
        queue.offer(23);
        queue.offer(24);
        queue.offer(25);
        queue.offer(26);

        Object[] elementData = queue.elementData;
        Stream.of(elementData).forEach(ele -> System.out.println(ele));

        System.out.println("elementData = " + queue.poll());
        System.out.println("elementData = " + queue.poll());
        System.out.println("elementData = " + queue.poll());
        Stream.of(queue.elementData).forEach(ele -> System.out.println(ele));
    }
}

2. 使用双向链表实现队列:

package com.feifei.demo.queue;


/**
 * 自定义链表队列(链表为双向链表),理论上是没有长度限制的
 */
public class LinkedQueue<E> {

    /**
     * 定义队首
     */
    private Node<E> front;

    /**
     * 定义队尾
     */
    private Node<E> rear;

    /**
     * 定义数据长度
     */
    private int size;

    /**
     * 定义节点类
     * @param <E>
     */
    private class Node<E> {

        private E element;

        private Node<E> preNode;

        private Node<E> nextNode;
    }

    public LinkedQueue() {
        front = rear = new Node<>();
    }

    /**
     * 获取数据长度
     * @return
     */
    public int size() {
        return size;
    }

    /**
     * 入队
     *
     * @param element
     * @return
     */
    public boolean offer(E element) {
        Node<E> eNode = new Node<>();
        eNode.element = element;
        // 当队列为空时
        if (rear == front) {
            front = eNode;
            rear.preNode = eNode;
        } else {
            Node<E> preNode = rear.preNode;
            preNode.nextNode = eNode;
            eNode.preNode = preNode;
            rear.preNode = eNode;
            System.out.println("添加成功");
        }
        size++;

        return true;
    }


    /**
     * 出队
     *
     * @return
     */
    public E pop() {
        if (rear == front) {
            System.out.println("队列为空,无法进行删除操作");
        }

        E element = front.element;
        Node<E> nextNode = front.nextNode;
        // 置空处理,方便GC回收
        front.preNode = null;
        front.nextNode = null;
        front.element = null;
        // 将下一个节点作为队首
        front = nextNode;
        size--;
        return element;
    }

    public static void main(String[] args) {
        LinkedQueue<Integer> linkedQueue = new LinkedQueue<>();
        linkedQueue.offer(1);
        linkedQueue.offer(2);
        linkedQueue.offer(3);
        linkedQueue.offer(4);
        linkedQueue.offer(5);

        System.out.println("linkedQueue pop = " + linkedQueue.pop());
        System.out.println("linkedQueue pop = " + linkedQueue.pop());
        System.out.println("linkedQueue pop = " + linkedQueue.pop());
        System.out.println("linkedQueue pop = " + linkedQueue.pop());
        System.out.println("linkedQueue pop = " + linkedQueue.pop());
    }
}

 

/* * 基于双向链表实现双端队列结构 */ package dsa; public class Deque_DLNode implements Deque { protected DLNode header;//指向头节点(哨兵) protected DLNode trailer;//指向尾节点(哨兵) protected int size;//队列中元素的数目 //构造函数 public Deque_DLNode() { header = new DLNode(); trailer = new DLNode(); header.setNext(trailer); trailer.setPrev(header); size = 0; } //返回队列中元素数目 public int getSize() { return size; } //判断队列是否为空 public boolean isEmpty() { return (0 == size) ? true : false; } //取首元素(但不删除) public Object first() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return header.getNext().getElem(); } //取末元素(但不删除) public Object last() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return trailer.getPrev().getElem(); } //在队列前端插入新节点 public void insertFirst(Object obj) { DLNode second = header.getNext(); DLNode first = new DLNode(obj, header, second); second.setPrev(first); header.setNext(first); size++; } //在队列后端插入新节点 public void insertLast(Object obj) { DLNode second = trailer.getPrev(); DLNode first = new DLNode(obj, second, trailer); second.setNext(first); trailer.setPrev(first); size++; } //删除首节点 public Object removeFirst() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = header.getNext(); DLNode second = first.getNext(); Object obj = first.getElem(); header.setNext(second); second.setPrev(header); size--; return(obj); } //删除末节点 public Object removeLast() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = trailer.getPrev(); DLNode second = first.getPrev(); Object obj = first.getElem(); trailer.setPrev(second); second.setNext(trailer); size--; return(obj); } //遍历 public void Traversal() { DLNode p = header.getNext(); while (p != trailer) { System.out.print(p.getElem()+" "); p = p.getNex
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值