数据结构---循环队列与循环双端队列的实现(Java实现)

本文详细介绍了循环队列和循环双端队列的实现原理,包括如何利用循环数组解决空间假溢出问题,以及如何通过count、预留空位或标志位来区分队列的满与空。同时,文章还探讨了循环双端队列的特性,允许在队头和队尾同时进行插入和删除操作。通过对成员变量、构造方法和核心操作的展示,如入队、出队、获取队头队尾元素等,读者可以全面理解这两种数据结构的实现细节。
摘要由CSDN通过智能技术生成

目录

🚁分析如何设计循环队列

🚀如何区分循环队列的满与空?

🛸实现循环队列 

🪂了解双端队列Deque

🛰️循环双端队列的实现


🚁分析如何设计循环队列

队列底层用双向链表实现,因为使用双向链表保证了入队列出队列的时间复杂度都达到O(1)那能否使用一段连续的空间实现呢?当然可以,先分析用普通的数组对其实现进行分析,看看会出现哪些问题?

front标记对头元素,进行出队列,用rear标记队尾后的空位置,进行入队列

🚧入队列操作:直接在rear标记的位置进行插入,再进行rear++,时间复杂度为O(1)

🏁出队列操作:有两种实现方式
方式1:将front位置的元素出队列后,front位置不变,将front后面的元素都向前搬移一个长度,时间复杂度O(n)
方式2:将front位置元素出队列后,再进行front++,时间复杂度O(1),但是会有一个问题,造成空间的假溢出

为了解决上述空间的假溢出问题,使用循环数组来实现循环队列,所谓循环数组,就是数组尾部标记往后再走的话就走到数组头部了

为了更好的理解循环队列,我们这样画图表示:

但是这样会出现一个问题:发现当rear与front在同一位置时,有两种状态,队列满,队列空,那么如何判断队列满与队列空❓

🤔思考怎么区分队列满与队列空

🚀如何区分循环队列的满与空?

有三种办法来区分循环队列的满与空

🔥方法一:使用count来标记队列中元素的个数,当count为0说明队列为空,当count等于数组的长度时说明队列已经满了
💧方法二:预留一个空位置,也就是当队列还有一个空位置的时候,认为该循环队列已经满了

⚡方法三:使用一个标记flag,初始时设置为false,每入队一个元素将flag设置为true

对比上述三种实现方式,使用count记录队列元素的个数辨识度更高 ,所以后续在实现的时候就使用count标记来判断队列的空与满

🛸实现循环队列 

循环队列的成员变量:

    private E[] array;
    private int front; //标记队列头,进行出队列
    private int rear; //标记队列尾,进行入队列
    private int count; //队列中元素个数,用来区分队列满与空
    private int N; //数组的长度

循环队列的构造方法:

    public MyCircularQueue(){
        array = (E[])new Object[10];
        N = array.length;
    }

入队列操作:

1. 如果队列已经满了,返回false
2. 否则在队尾位置直接插入元素,count++,队尾标记rear往后走一个
3.  如果rear走到N,则将rear置为0,从头开始继续

    public boolean enQueue(E val){
        if(isFull()){
            return false;
        }
        array[rear] = val;
        rear++;
        if(rear == N){
            rear = 0;
        }
        count++;
        return true;
    }

出队列操作:

1. 如果队列已经为空,则返回false
2. 否则直接将count--,front++
3. 如果front走到N的位置,将front置为0,从头开始,与上述类似

    public boolean deQueue(){
        if(isEmpty()){
            return false;
        }
        count--;
        front++;
        if(front == N){
            front = 0;
        }
        return true;
    }

获取队尾元素:

如果队列为空抛出异常,否则直接返回rear前一个位置的元素,因为rear标记的是队尾的空位置,对rear的处理有两种情况,所以要对两种情况统一处理,获取(rear-1+N)%N位置元素

    public E rear(){
        if(isEmpty()){
            throw new RuntimeException("队列为空");
        }
        return array[(rear-1+N)%N];
    }

获取对头元素:

如果队列为空,则抛出异常,否则直接返回front位置元素,因为front标记的是队头元素

    public E front(){
        if(isEmpty()){
            throw new RuntimeException("队列为空");
        }
        return array[front];
    }

判断队列是否为空:

比较count==0

    public boolean isEmpty(){
        return count==0;
    }

判断是否队列已经满了: 

比较count==N

    public boolean isFull(){
        return count==N;
    }

🪂了解双端队列Deque

Deque是一个接口,底层也是用LinkedList实现的,双端队列指队列的两端都可以进行入队列和出队列操作

🛰️循环双端队列的实现

循环双端队列,与循环队列的实现大同小异,只不过是在队列头多了入队列操作,在队列尾多了出队列操作

🚨注意:

在循环队列中,front标记对头元素,rear标记队尾空位置
在循环双端队列中,front标记队头空位置,rear标记队尾元素 

循环双端队列的成员变量:

    int[] array;
    int rear; //队尾
    int front; //队头
    int count; //队列中元素个数
    int N; //数组长度

循环双端队列的构造方法:

    public MyCircularDeque() {
        array = new int[10];
        N = array.length;
    }

队列头部插入:

1. 如果队列已经满了,直接返回false
2. 否则在front位置直接插入元素,然后front--,count++
3. 如果front当前为0,front--直接就为-1,所以要进行(front+N)%N计算更新front

    public boolean insertFront(int value) {
        if(isFull()){
            return false;
        }
        array[front] = value;
        count++;
        front--;
        front = (front+N)%N;
        return true;
    }

队列尾部插入:

1. 如果队列为空,则直接返回false
2. 否则在rear+1位置插入元素,如果rear+1等于数组长度时,需要置0,所以进行rear%N操作
3. 插入后count++ 

    public boolean insertLast(int value) {
        if(isFull()){
            return false;
        }
        rear++;
		rear %= N;
		array[rear] = value;
		count++;
		return true;
    }

队列头部删除:

1. 如果队列为空,直接返回false
2. 否则将front往后走一个位置,如果走到N的位置,则需要将front重新置0,所以(front+1)%N
3. 删除完,count-- 

    public boolean deleteFront() {
        if(isEmpty()){
            return false;
        }
        front = (front+1)%N;
        count--;
        return true;
    }

队列尾部删除:

1. 如果队列为空,则直接返回false
2. 否则rear往前走一个位置,如果走到-1,则要将rear置为末尾,所以(rear-1+N)%N
3. 删除后,count--

    public boolean deleteLast() {
        if(isEmpty()){
            return false;
        }
        rear = (rear-1+N)%N;
        count--;
        return true;
    }

获取队头元素:

1. 如果队列为空,则返回-1
2. 否则返回front下一个位置的元素,如果front下一个位置为N,则将front置为0,所以(front+1)%N
3. 返回array[(front+1)%N] 

    public int getFront() {
        if(isEmpty()){
            return -1;
        }
        return array[(front+1)%N];
    }

获取队尾元素:

1. 如果队列为空,则直接返回-1
2. 直接返回rear位置的元素 

    public int getRear() {
        if(isEmpty()){
            return -1;
        }
        return array[rear];
    }

判断队列是否为空:

直接判断count==0 

    public boolean isEmpty() {
        return count==0;
    }

判断队列是否已经满了: 

直接判断count==N 

    public boolean isFull() {
        return count==N;
    }
/* * 基于双向链表实现双端队列结构 */ 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
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X_H学Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值