数据结构之数组环形队列(Java实现)

数组模拟队列时,会有不能复用的问题,这里使用环形队列来解决。

因为模拟的是环形队列,所以我们调整一下队列中各个属性的含义。

1.分析:

 

原来的非循环队列(非环形队列) ,在rear这个尾部指针到达rear==maxSize-1时,就不能再存放数据了,就算我们把数据给取出来,也只是在逻辑上把这个数据给取出来了,真实的情况是,这个数据取出来后,在队列中还有该数据,这个问题应该如何解决呢?答案是---环形队列。

所以我们要把这个队列给进行改进,把这个队列改成一个环形队列(循环队列),从而使该队列能够达到一个复用的效果。

1.环形队列的思想就是,当rear指针达到该队列的最大容量时,rear继续递增 从尾部到头部,从头部增加到尾部,这样一直循环存放数据。

2.但是如果是按照非循环队列(上篇数组模拟队列有说明)的条件rear==front来判断该队列是否存满,显然是无法判定的,因为刚开始没有数据的时候rear==front表明这个队列是空的。但是环形队列是一直递增的,当rear一直递增 增加到了头部也就是front的位置时,这个时候明明是有数据的,此时的rear也和front相等。所以按照肺循环队列的条件来是无法判定环形队列(循环队列)是否已存满的。

3.此时我们制定一个约定:我们牺牲环形队列中的一个空间 以便我们来判断该环形 队列是否已满。

  • 因为我们预留了一个空间,所以rear这个属性调整为应该指向该队列最后一个元素的后一个的位置。也就是此时的rear=rear+1

  • rear的属性值的初始值为0

  • front这个属性调整为指向该队列头部的第一个元素。

  • front属性的初始值为0

  • 因为rear是一直递增的,所以我们要对maxSize取余才可以得到rear的一个真实位置,所以此时判断队满的条件就是(rear+1)%maxSize==front 【队满】

  • 队空条件是:rear==front。

  • 我们这样分析时,队列中的有效数据个数就是 (rear+maxSize-front)%maxSize //如上图:rear=4,maxSize=5,front=0,有效数据个数就是:(4+5-0)%5==4个 上图中的有效数据个数正好是4个。

  • 我们只需要在非循环队列(非环形队列)代码的基础上进行更改,就可以实现环形队列了。

package com.lzh.queue;

import java.util.Scanner;

public class CircleArrayQueue {
    public static void main(String[] args) {
        //测试--创建一个队列
        CircleQueue circleQueue = new CircleQueue(4);
        char key = ' ';//接收用户输入
        Scanner scanner = new Scanner(System.in);
        boolean loop = true;
        while (loop) {

            System.out.println("s(show):显示队列");
            System.out.println("e(exit):退出程序");
            System.out.println("a(add):添加数据到队列");
            System.out.println("g(get):从队列取出数据");
            System.out.println("h(head):查看队列头的数据");
            //key=scanner.next().charAt(0); 要放在这个位置
            key = scanner.next().charAt(0); //接收用户输入的第一个字符
            switch (key) {
                case 's':
                    circleQueue.showAll();
                    break;
                case 'e' :
                    scanner.close();
                    loop = false;
                    System.out.println("程序退出");
                    break;
                case 'a' :
                    System.out.println("请输入一个整数");
                    int a = scanner.nextInt();
                    circleQueue.addQueue(a);
                    break;
                case 'g' :
                    try {
                        int res = circleQueue.getQueue();
                        System.out.println(res);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }

                    break;
                case 'h' :
                    try{
                        int head = circleQueue.getHead();
                        System.out.println(head);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;

            }


        }
    }

}

//使用数组模拟环形队列
class CircleQueue{
    //front和rear的初始值设置为0
    private int maxSize;//数组的最大容量
    //front 默认值为0
    private int front;//指向队列的第一个元素
    private int rear;//指向队列最后一个元素的后一个位置
    private int[] arr;//该数组存放数据,用数组模拟队列

    //创建一个构造方法
    public CircleQueue(int arrayMaxSize){
        maxSize = arrayMaxSize;
        arr = new int[maxSize];
        //front和rear不用赋值了,因为默认值就是0
    }
    //判断队列是否为空
    public boolean isNull(){
       return rear == front;
    }
    //判断队列是否已经存满了数据
    public boolean isFull(){
        return (rear+1)%maxSize == front;
    }
    //把数据加入队列
    public void addQueue(int n){
        //先判断这个队列是否已经满了
        if(isFull()){
            System.out.println("该队列已满,不能再添加数据了");
            return;
        }
        //如果没满,则执行下面的代码
        //因为rear是指向最后一个元素的后一个位置,所以这个地方的位置一定是空的,所以可以直接添加数据
        arr[rear] = n;
        //添加完之后,指针向后移,但是如果是环形队列的话,rear一直加可能会下标越界,所以这里用取模
        rear=(rear+1)%maxSize;
    }
    //从队列中取出数据
    public int getQueue(){
        //先判断该队列是否为空
        if(isNull()){
            throw  new RuntimeException("该队列为空,不能取出数据");
        }
        //因为front是指向该队列的第一个元素,所以可以直接取到
        //用一个中间变量来接收取到的数据,然后再进行front后移,不然front无法后移
        int value = arr[front];
        //front也是一样,一直递增会造成下标越界,所以我们同样做一个取模计算
        front = (front+1)%maxSize;
        return value;
    }
    //显示头部数据
    public int getHead(){
        //先判断该队列是否为空
        if(isNull()){
            throw  new RuntimeException("该队列为空,得不到头部数据");
        }
        return arr[front];
    }
    //显示所有数据
    public void showAll(){
        //先判断该队列是否为空
        if (isNull()) {
            System.out.println("该队列为空,取不到数据");
            return;
        }
        //这里我们打印出所有数据,但是已经空的位置不用打印,所以我们让i=front,从第一个元素开始
        //而这个front+size() 表示第一个元素的下标+多少个有效元素,取代了之前的i的范围
        //比如front==1,size()==3 ,maxSize=5 则这个循环执行3次
        for (int i = front;i<front+size();i++){
            //这里为什么进行取模,因为i==front ,i<front+size()可能会下标越界,所以我们进行取模
            System.out.printf("arr[%d]=%d\n", i%maxSize, arr[i%maxSize]);
        }
    }
    //得到该队列有多少有效数据
    public int size(){
        return (rear+maxSize-front)%maxSize;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值