小白的数据结构学习—2.队列的数组实现之循环队列

157 阅读5分钟

定义:队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

即遵循先进先出的原则

那么队列怎么样才能够循环利用呢?这时候就引出了循环队列的概念,但是在实现循环队列之前,先讲一讲普通的由数组实现的队列。

队列之普通数组实现篇

设置一个队列,用数组实现,front表示队列的前端,rear表示数组的后端,他们的初始值都为-1;当向队列中加入数据data时,rear++,arr[rear]=data,假设当加入的data=10,此时arr[0]=10,rear指向0处。当rear指向6时,即rear=maxSize-1时,此时队列就满了。当向数组中取值时,front++,return arr[front],front向上移动。

下面使用代码实现:

class ArrayQueue{
   private int maxSize;//数组长度,即队列长度
   private int front;//队列头
   private int rear;//队列尾
   private int[] arr;//数组
   public ArrayQueue(int maxSize){
       this.maxSize=maxSize;
       front=-1;
       rear=-1;
       arr=new int[maxSize];
   }
   //判断是否为空
   public boolean isEmpty(){
       return front==rear;
   }
   //判断是否满了
   public boolean isFull(){
       return rear==maxSize-1;
   }
   //向队列中加入数据
   public void add(int data){
       if(isFull()){
           System.out.println("队列已满");
           return;
       }
       rear++;
       arr[rear]=data;
   }
   //像队列中取出数据
   public int get(){
       if(isEmpty()){
         throw new RuntimeException("队列是空的");
       }
       front++;
       return arr[front];
   }
   //显示队列中的所有数据
   public void show(){
       if(isEmpty()){
           System.out.println("队列是空的");
           return;
       }
       for (int i=front+1;i<rear+1;i++){
           System.out.println("arr["+i+"]"+arr[i]);
       }
   }
   //显示队列的头元素
   public void headQueue(){
       if(isEmpty()){
           throw new RuntimeException("队列是空的");
       }
       System.out.println("arr["+(front+1)+"]"+arr[front+1]);


   }

然后创建一个程序运行一下这个普通的数组队列吧!

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        ArrayQueue arrayQueue=new ArrayQueue(3);
        char c=' ';
        boolean flag=true;
        while (flag){
            System.out.println("a(add)");
            System.out.println("g(get)");
            System.out.println("s(show)");
            System.out.println("h(head)");
            System.out.println("e(exit)");
            c = sc.next().charAt(0);
            switch (c){
                case 'a':
                    System.out.println("请输入一个数");
                    int num = sc.nextInt();
                    arrayQueue.add(num);
                    break;
                case 'g':
                    try {
                        arrayQueue.get();
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }

                    break;
                case 's':
                    arrayQueue.show();
                    break;
                case 'h':
                    try {
                        arrayQueue.headQueue();
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }

                    break;
                case 'e':
                    flag=false;
                    break;
            }
        }
    }

这里测试的时候为了简单,将队列的size设置为3.

从程序运行的结果来看,当队列满了之后,即使从队列中取出了数据,队列中有了空的位置,但是仍然不能够继续加入数据,这样的队列只能使用一次。不能满足我们对队列的要求,所以便需要有一个能够不断存储的队列,下文便实现循环队列

队列之循环队列实现篇

先看一张图

如图所示需要留一个位置为空置位,当front对应的数据取出时,加入数据的时候,rear指向下一个地址,此时这个空置位填入数据,此时rear指向的地址是空置位,即这个空置位是动态变化的 下面是代码实现:

class CircleArrayQueue{
    private int front;
    private int rear;
    private int maxSize;
    private int []arr;
    public CircleArrayQueue(int maxSize){
        this.maxSize=maxSize;
        arr=new int[maxSize];
    }
    public boolean isEmpty(){
        return front==rear;
    }
    public boolean isFull(){
        return (rear+1)%maxSize==front;
    }
    public void add(int data){
        if(isFull()){
            System.out.println("队列已满");
            return;
        }
        arr[rear]=data;
       rear= (rear+1)%maxSize;
    }
    public int get(){
        if(isEmpty()){
            throw new RuntimeException("队列是空的");
        }
        int data=arr[front];
        front=(front+1)%maxSize;
        return data;
    }

    //需要计算出队列中的个数
    public void show(){
        if (isEmpty()){
            System.out.println("队列是空的");
            return;
        }
        for (int i=front;i<front+getNum();i++){
            System.out.println("arr["+(i%maxSize)+"]"+arr[i%maxSize]);
        }
    }

    /*当队列不是循环数组的时候,个数为rear-front
    * 但是当为循环数组时,rear-front的值可能为负数
    * 所以此时应该加上maxSize,
    * 但同时如果rear-front的值为正数的时候,加上maxSIze的值超出了数组的范围
    * 所以此时需要与maxSize取余*/
    public int getNum(){
        return (rear-front+maxSize)%maxSize;
    }

    public void headQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列是空的");
        }
        System.out.println("队列的头元素为"+arr[front]);
    }
}

可以将循环数组队列的代码与普通数组队列的代码相互比较,可以 看出:

1.判断是否为空的方法是相同的;

2.判断是否为满的代码有了区别,因为普通数组队列是一次性的,当rear到达了maxSize-1时便队列为满,而循坏数组的队列需要判断rear下一个地址的值是否为空,如果为空,则数组没有满。并且要考虑到rear下一个地址是否超过了队列的范围,如果超过就会指到0地址处,即(rear+1)%maxSize是否等于maxSize;

3.add方法和show方法有一点区别,最初让front和rear都指向0,当rear和front都为maxSize-1时,继续增加让它们指向队列的0地址处。

4.headQueue方法无区别

创建程序的代码和前面的相同。 下面为程序运行的截图

从这里便可以看出循环队列实现了队列的不断增加与删除

下面是循环队列全文代码
public class CircleArrayQueueDemo {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        CircleArrayQueue arrayQueue=new CircleArrayQueue(4);
        char c=' ';
        boolean flag=true;
        System.out.println("a(add)");
        System.out.println("g(get)");
        System.out.println("s(show)");
        System.out.println("h(head)");
        System.out.println("e(exit)");
        while (flag){

            c = sc.next().charAt(0);
            switch (c){
                case 'a':
                    System.out.println("请输入一个数");
                    int num = sc.nextInt();
                    arrayQueue.add(num);
                    break;
                case 'g':
                    try {
                        arrayQueue.get();
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }

                    break;
                case 's':
                    arrayQueue.show();
                    break;
                case 'h':
                    try {
                        arrayQueue.headQueue();
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }

                    break;
                case 'e':
                    flag=false;
                    break;
            }
        }

    }
}
class CircleArrayQueue{
    private int front;
    private int rear;
    private int maxSize;
    private int []arr;
    public CircleArrayQueue(int maxSize){
        this.maxSize=maxSize;
        arr=new int[maxSize];
    }
    public boolean isEmpty(){
        return front==rear;
    }
    public boolean isFull(){
        return (rear+1)%maxSize==front;
    }
    public void add(int data){
        if(isFull()){
            System.out.println("队列已满");
            return;
        }
        arr[rear]=data;
       rear= (rear+1)%maxSize;
    }
    public int get(){
        if(isEmpty()){
            throw new RuntimeException("队列是空的");
        }
        int data=arr[front];
        front=(front+1)%maxSize;
        return data;
    }

    //需要计算出队列中的个数
    public void show(){
        if (isEmpty()){
            System.out.println("队列是空的");
            return;
        }
        for (int i=front;i<front+getNum();i++){
            System.out.println("arr["+(i%maxSize)+"]"+arr[i%maxSize]);
        }
    }

    /*当队列不是循环数组的时候,个数为rear-front
    * 但是当为循环数组时,rear-front的值可能为负数
    * 所以此时应该加上maxSize,
    * 但同时如果rear-front的值为正数的时候,加上maxSIze的值超出了数组的范围
    * 所以此时需要与maxSize取余*/
    public int getNum(){
        return (rear-front+maxSize)%maxSize;
    }

    public void headQueue(){
        if(isEmpty()){
            throw new RuntimeException("队列是空的");
        }
        System.out.println("队列的头元素为"+arr[front]);
    }
}