队列:图解队列-Java实现 浅显易懂

队列(queue)

队列先进先出( FIFO,First In, First Out) 的线性表。在具体应用中通常用链表或者数组来实现,用数组实现的队列叫作 顺序队列 ,用链表实现的队列叫作 链式队列队列只允许在后端(rear)进行插入操作也就是 入队 enqueue,在前端(front)进行删除操作也就是出队 dequeue

顺序队列

数组实现的队列叫顺序队列

用front来标记与队头元素位置的关系
用rear来标记与队尾元素位置的关系

如下:当队列为空时,设置front=rear=-1

-1为初始值,不代表索引), 当添加元素时:

队列:图解队列-Java实现 浅显易懂

rear=数组长度-1时该队列已满。此时rear=maxSize-1;front还是在队头的前面。

删除元素:

队列:图解队列-Java实现 浅显易懂

注意: 所谓的删除,其实就是通过操作front和rear与队头队尾元素索引的关系,使我们访问不到元素,实际上元素还在队列里面

public class ArrayQueueDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        ArrayQueue arrayQueue = new ArrayQueue(3);

        boolean flag=true;
        while (flag) {
            System.out.println("请输入:(s:显示队列,a:添加队列,g:取出数据,h:查看队头数据,e:退出)");
            switch (sc.next().charAt(0)) {
                case 's':
                    try {
                        arrayQueue.listQueue();
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'a':
                    System.out.println("请输入:");
                    try {
                        arrayQueue.addQueue(sc.nextInt());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'g':
                    try {
                        int queue = arrayQueue.getQueue();
                        System.out.println(queue);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int i = arrayQueue.headQueue();
                        System.out.println(i);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    flag=false;
                    break;
                default:
                    System.out.println("输入有误!!");
                    break;
            }
        }
    }
}
class ArrayQueue{ // 数组实现队列
    private int maxSize; // 数组容量
    private int front; // 队列头
    private int rear; // 队列尾
    private int[] arr; // 模拟队列的数组

    //队列构造器
    public ArrayQueue(int arrMaxSize){
        maxSize=arrMaxSize;
        arr=new int[arrMaxSize];
        front=-1; // 记录队列头索引的前一个位置
        rear=-1; // 记录队列尾索引
    }

    //判断队列是否为空
    public boolean isEmpty() {
        return rear==front;
    }
    //判断队列是否满
    public boolean isFull() {
        return rear==maxSize-1;
    }
    // 添加数据到队列
    public void addQueue(int n){
        //判断是否满
        if (isFull()){
            throw new RuntimeException("队列满,不能添加数据");
        }
        rear++; //后移1位
        arr[rear]=n;
    }
    //取出队列数据,出队列
    public int getQueue() {
        //判断队列是否为空
        if (isEmpty()){
            throw new RuntimeException("队列空,不能获取数据");
        }
        front++; // 后移
        return arr[front];
    }
    //显示队列所有元素
    public void listQueue() {
        //判空
        if (isEmpty()) {
            throw new RuntimeException("队列空,无数据");
        }
        for (int i = 0; i < arr.length; i++) {
            if (i==0){
                System.out.print("[ "+arr[i]);
            }else if (i==maxSize-1){
                System.out.print(","+arr[i]+" ]");
            }else {
                System.out.print(","+arr[i]);
            }
        }
    }
    //显示头数据
    public int headQueue() {
        if (isEmpty()){
            throw new RuntimeException("队列为空,无数据");
        }
        return arr[front+1];
    }
}

队列:图解队列-Java实现 浅显易懂

缺点:使队列空间只能使用一次,不能重复使用。

这种情况称为"假溢出"(入队和出队操作中,头尾指针只增加不减小,致使被删元素的空间永远无法重新利用,队列队列满时rear=maxSize-1,还要添加元素,则rear+1就会>数组的索引)

队列:图解队列-Java实现 浅显易懂

环形队列(数组)

在实际使用队列时,为了使队列空间能重复使用,往往对队列的使用方法稍加改进:无论插入或删除,一旦rear指针增1或front指针增1 时超出了所分配的队列空间,就让它指向这片连续空间的起始位置。
在循环队列中,当队列为空时,有front=rear,而当所有队列空间全占满时,也有front=rear。为了区别这两种情况,规定循环队列最多只能有MaxSize-1个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。因此,队列判空的条件时front=rear,而队列判满的条件时front=(rear+1)%MaxSize

队列:图解队列-Java实现 浅显易懂

队列中有效数据个数为:(rear+maxSize-front)%maxSize,如上图:(3+4-0)%4=3

实现环形队列:

取出元素front=(front+1)%maxSize,当front到队列末尾时有回到起点:front=0,形成循环

队列:图解队列-Java实现 浅显易懂

添加元素:rear=(rear+1)%maxSize,当队列满时,rear=0,回到起点形成循环

队列:图解队列-Java实现 浅显易懂
package com.pqy.linear.queue;

import java.util.Scanner;

/**
 * @author panqiyi
 * @date 2021/9/25 - 15:20
 */
class CircleArray {
    private int maxSize; // 数组容量
    private int front; // 队列头,默认0
    private int rear; // 队列尾
    private int[] arr; // 模拟队列的数组

    //队列构造器
    public CircleArray(int arrMaxSize) {
        maxSize = arrMaxSize;
        arr = new int[arrMaxSize];
    }

    //判断队列是否为空
    public boolean isEmpty() {
        return rear == front;
    }

    //判断队列是否满
    public boolean isFull() {
        return (rear+1)%maxSize==front;
    }

    // 添加数据到队列
    public void addQueue(int n) {
        //判断是否满
        if (isFull()) {
            throw new RuntimeException("队列满,不能添加数据");
        }
        arr[rear] = n;
        rear=(rear+1)%maxSize; // 后移取模
    }

    //取出队列数据,出队列
    public int getQueue() {
        //判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列空,不能获取数据");
        }
        int value=arr[front];
        front=(front+1)%maxSize; // 后移取模
        return value;
    }

    //显示队列所有元素
    public void listQueue() {
        //判空
        if (isEmpty()) {
            throw new RuntimeException("队列空,无数据");
        }
        for (int i = front; i < front+size(); i++) { // 队头+有效元素个数
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
        }
    }
    //求有效数据个数
    public int size(){
        return (rear+maxSize-front)%maxSize;
    }

    //显示头数据
    public int headQueue() {
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无数据");
        }
        return arr[front];
    }
}

public class CircleArrayQueueDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        CircleArray circleArray = new CircleArray(3);

        boolean flag=true;
        while (flag) {
            System.out.println("请输入:(s:显示队列,a:添加队列,g:取出数据,h:查看队头数据,e:退出)");
            switch (sc.next().charAt(0)) {
                case 's':
                    try {
                        circleArray.listQueue();
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'a':
                    System.out.println("请输入:");
                    try {
                        circleArray.addQueue(sc.nextInt());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'g':
                    try {
                        int queue = circleArray.getQueue();
                        System.out.println(queue);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h':
                    try {
                        int i = circleArray.headQueue();
                        System.out.println(i);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    flag=false;
                    break;
                default:
                    System.out.println("输入有误!!");
                    break;
            }
        }
    }
}

运行:

2、设置数组大小为3

2、输入 11,22

队列:图解队列-Java实现 浅显易懂

队列:图解队列-Java实现 浅显易懂

3、取出一个元素

队列:图解队列-Java实现 浅显易懂

队列:图解队列-Java实现 浅显易懂

4、添加一个元素

// 添加数据到队列
    public void addQueue(int n) {
        //判断是否满
        if (isFull()) {
            throw new RuntimeException("队列满,不能添加数据");
        }
        arr[rear] = n; //添加元素
        rear=(rear+1)%maxSize; // 后移取模
    }
队列:图解队列-Java实现 浅显易懂

5、查看队列

队列:图解队列-Java实现 浅显易懂

 //显示队列所有元素
    public void listQueue() {
        //判空
        if (isEmpty()) {
            throw new RuntimeException("队列空,无数据");
        }
        for (int i = front; i < front+size(); i++) { // 队头开始到 队头+有效元素个数
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
        }
    }
    //求有效数据个数
    public int size(){
        return (rear+maxSize-front)%maxSize;
    }

为什么要 i%maxSize ? 因为这样才能得到准确的元素索引(再删除添加一个元素就知道了,添加44)

队列:图解队列-Java实现 浅显易懂

再查看

队列:图解队列-Java实现 浅显易懂

因为是循环队列,所有遍历有效的数据,front到front+size()可能是在不同方向的,front与rear都是取模的,所以索引也取模才能得到。

上一篇:数据结构 队列


下一篇:队列溢出问题