队列、环形队列

19 年 8 月 19 日 星期一
1874 字
10 分钟

数据结构与算法

单队列ArrayQueue环形队列CircleArrayQueue

1. 单队列

  • 队列是一个有序列表,可以使用数组或链表来实现。

  • 先入先出,先存入队列的数据先取出,如排队。

单队列的3个属性:

  • MaxSize表示队列最大容量
  • rear表示尾指针,指向队列末尾,记录该数据的下标,随着数据输入而改变。
  • front表示头指针,一般让它指向队列头的前一个位置,记录队列头的前一个下标,先自增后取值。

单队列生命周期

开始时,rear与front均指向-1,这是初始状态。

每把一个数据放入队列,尾指针rear就会+1。

当rear=MaxSize-1时,队列满。

每把一个数据从队列中取出,头指针front+1。

当front==rear时,即头指针与尾指针指向同一处,此时队列为空。

无法再取,所以,front<=rear。

单队列使用完一次后,便不能再次使用。

单队列数组实现

java
package Queue;

import java.util.Scanner;

public class ArrayQueue {

    private int MaxSize;
    private int front;
    private int rear;
    private int[] array;

    //初始化数组
    ArrayQueue(int MaxSize){

        this.MaxSize=MaxSize;
        array=new int[MaxSize];
        front=-1;
        rear=-1;

    }

    //判断队列是否满
     boolean isFull(){
        return rear==MaxSize-1;
    }

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

    //向队列增加数据
    void add(int data){

        if (isFull()){//判断队列是否已满
            System.out.println("队列已满,无法存");
        }else {
            rear++;//尾指针后移
            array[rear]=data;//赋值
            System.out.println("尾指针+1:"+rear);
        }

    }

    //从队列取数据
    int get(){

       if (isEmpty()){//判断队列是否为空
           throw new RuntimeException("队列为空,无法取");
       }else {
           front++;
           System.out.println("头指针+1:"+front);

           return array[front];//取值
       }

    }

    //显示整个队列
    void show(){
        if (isEmpty()){//判断队列是否为空
            throw new RuntimeException("队列空,无数据");
        }else {
            for (int data:array){//打印数组
             System.out.print(data+"\t");
            }
            System.out.println();
        }

    }

    //展示队列头数据
    int showFront(){
        if (isEmpty()){//判断队列是否为空
            throw new RuntimeException("队列空,无数据");
        }else {
           return array[front+1];//返回头数据,指针不变
            }
        }


    //展示队列尾数据
    int showRear(){
        if (isEmpty()){//判断队列是否为空
            throw new RuntimeException("队列空,无数据");
        }else {
            return array[rear];//返回尾数据,指针不变
        }

    }

    //测试队列
    public static void main(String[] args) {
        //创建一个队列对象,容量为3
        ArrayQueue queue = new ArrayQueue(3);
        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): 查看队列头的数据");
            System.out.println("r(rear): 查看队列尾的数据");
            key = scanner.next().charAt(0);//接收一个字符
            switch (key) {
                case 's':
                    queue.show();
                    break;
                case 'a':
                    System.out.println("输入一个数");
                    int value = scanner.nextInt();
                    queue.add(value);
                    break;
                case 'g': //取出数据
                    try {
                        int res = queue.get();
                        System.out.printf("取出的数据是%d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h': //查看队列头的数据
                    try {
                        int res = queue.showFront();
                        System.out.printf("队列头的数据是%d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'r': //查看队列头的数据
                    try {
                        int rear = queue.showRear();
                        System.out.printf("队列尾的数据是%d\n", rear);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e': //退出
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出~~");
    }
}

2. 环形队列

相比单队列,环形队列属性设定有所变动:

属性的设定

  • 初始时front = rear = 0,而不是-1

  • rear指向队列尾数据的下一个空间,在尾数据后预留一个空间,即rear指向的位置不放值。(预留空间是为了区分队列空和队列满,空元素作为队尾的标志)

  • front直接指向头数据

  • 容量为MaxSize-1

环形队列生命周期

开始时,rear与front均指向0,这是初始状态。

每把一个数据放入队列,尾指针rear就会+1。

当**(rear+1)%MaxSize=front** 时,队列满。因为rear指向尾数据的下一个空间,队列满时rear+1自然等于front。

之所以%MaxSize取模是因为:环形队列指针的值会一直增加,%Maxsize会得到周期性的值,就像角度会加到362度、750度一样。

温馨提示:

%运算符:比如2%3就是,2除以3等于0余2,所以2%3就是2,而3%2=1。

每把一个数据从队列中取出,头指针front+1。

当front==rear时,即头指针与尾指针指向同一处,此时队列为空。

重点

这里不再像单队列,在循环中,front与rear的大小关系是不定的,有时rear>front,有时front>rear。

为什么会出现这种情形呢,rear>front的情形好理解,尾与头嘛。至于front>rear的情形,我们类比一下角度,假如rear在361度的位置,front在365度的位置,别忘了是环形。

由此可以得出,环形队列中的元素总数为:(rear-front+MaxSize)%MaxSize,始终用尾减去头,又因为rear-front的值正负不定,所以要加上MaxSize%MaxSize

环形队列可循环使用。

遍历时,从front开始,一直到front+size结束,size是当前元素个数,因为元素个数size会随着出队进队变化,变化量与front的移动量是一致的

环形队列数组实现

java
package Queue;

import java.util.Scanner;

public class CircleQueue {

    private int MaxSize;
    private int front;
    private int rear;
    private int[] array;

    //初始化数组
    CircleQueue(int MaxSize){

        this.MaxSize=MaxSize;
        array=new int[MaxSize];
        front=0;
        rear=0;

    }

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

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

    //向队列增加数据
    void add(int data){

        if (isFull()){//判断队列是否已满
            System.out.println("队列已满,无法存");
        }else {
            array[rear%MaxSize]=data;//赋值
            rear++;//尾指针后移
            System.out.println("尾指针+1:"+rear%MaxSize);
        }

    }

    //从队列取数据
    int get(){

        if (isEmpty()){//判断队列是否为空
            throw new RuntimeException("队列为空,无法取");
        }else {
            int data=array[front%MaxSize];
            front++;
            System.out.println("头指针+1:"+front%MaxSize);
            return data;//取值
        }

    }

    //队列中的有效元素总数
    int getSize(){
        return (rear-front+MaxSize)%MaxSize;
    }


    //显示整个队列
    void show(){
        if (isEmpty()){//判断队列是否为空
            System.out.println("队列为空");
        }else {
            for (int i=front%MaxSize;i<front%MaxSize+getSize();i++){//打印数组
                System.out.println(array[i]+"\t");
            }
            System.out.println();
        }

    }

    //展示队列头数据
    int showFront(){
        if (isEmpty()){//判断队列是否为空
            throw new RuntimeException("队列空,无数据");
        }else {
            return array[front%MaxSize];//返回头数据,指针不变
        }
    }


    //展示队列尾数据
    int showRear(){
        if (isEmpty()){//判断队列是否为空
            throw new RuntimeException("队列空,无数据");
        }else {
            return array[(rear-1)%MaxSize];//返回尾数据,指针不变
        }

    }



    //测试队列
    public static void main(String[] args) {
        //创建一个队列对象,容量为5
        CircleQueue queue = new CircleQueue(5);
        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("z(getSize): 查看有效元素个数");
            System.out.println("h(head): 查看队列头的数据");
            System.out.println("r(rear): 查看队列尾的数据");
            key = scanner.next().charAt(0);//接收一个字符
            switch (key) {
                case 's':
                    queue.show();
                    break;
                case 'a':
                    System.out.println("输入一个数");
                    int value = scanner.nextInt();
                    queue.add(value);
                    break;
                case 'g': //取出数据
                    try {
                        int res = queue.get();
                        System.out.printf("取出的数据是%d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'h': //查看队列头的数据
                    try {
                        int res = queue.showFront();
                        System.out.printf("队列头的数据是%d\n", res);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'r': //查看队列头的数据
                    try {
                        int rear = queue.showRear();
                        System.out.printf("队列尾的数据是%d\n", rear);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                    case 'z': //查看队列头的数据
                    try {
                        int size = queue.getSize();
                        System.out.printf("元素个数是%d\n", size);
                    } catch (Exception e) {
                        // TODO: handle exception
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e': //退出
                    scanner.close();
                    loop = false;
                    break;
                default:
                    break;
            }
        }
        System.out.println("程序退出~~");
    }
}

文章标题:队列、环形队列

文章作者:shirtiny

文章链接:https://kizamu.anror.com/posts/data-structures-queue[复制]

最后修改时间:


商业转载请联系站长获得授权,非商业转载请注明本文出处及文章链接,您可以自由地在任何媒体以任何形式复制和分发作品,也可以修改和创作,但是分发衍生作品时必须采用相同的许可协议。
本文采用CC BY-NC-SA 4.0进行许可。