暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

环形队列设计

IT那活儿 2025-03-03
63

点击上方“IT那活儿”公众号--专注于企业全栈运维技术分享,不管IT什么活儿,干就完了!!!


队列及环形队列定义

队列是一种线性数据结构,它遵循先进先出或后近后出的原则。队列允许在一端插入元素,另一端删除元素。队列有时也被称为待办事项列表,尤其是在计算机程序中,它被广泛用于任务排队、缓存和异步处理方案中。

环形队列,它可以在固定大小的数组中循环使用。它类似于普通队列,但与普通队列不同的是,在环形队列中,队列的队尾可以接到数组的开头,使数组像一个圆环一样循环。因此,环形队列往往比普通队列的效率更高,尤其是在实现循环缓冲区等应用时。环形队列主要的操作有入队和出队。


环形队列的特点

环形队列是一种特殊的队列数据结构,它的特点如下:
  • 队列的存储结构是一个环形结构,即队列的头尾相连,形成一个环形。
  • 队列有固定的大小,通常用数组来实现。
  • 队列的插入和删除操作只能在队头和队尾进行,而不能在中间进行。
  • 当队列的头指针或尾指针达到队列的末尾时,它们将指向队列的起始位置,形成环形。
  • 环形队列的操作效率较高,只需要通过指针的移动来完成插入和删除操作。

设计背景和思路

3.1 设计背景

在计算指标生成的时候,当该时间段没有统计到该指标,就取他最近前两个指标的平均值,这样的话历史指标的保存和获取,就要用到一个队列来存储,考虑环形队列的特点,自动清理过期数据,使用效率高的特点,所以设计一个数组环形队列。

3.2 设计思路

  • front 

    就指向队列的第一个元素, front 的初始值 = 0;

  • rear 

    指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定,rear 的初始值 = 0;

    当队列满时,条件是 (rear + 1) % maxSize == front 【满】;

    对队列为空的条件, rear == front 【空】;

    队列中有效数据的个数 (rear + maxSize - front) % maxSize。


测试结果

ArrayQueue arrayQueue = new ArrayQueue(4); //初始化一个队列
arrayQueue.addQueue(1);
arrayQueue.addQueue(2);
arrayQueue.addQueue(3);
arrayQueue.addQueue(4);
arrayQueue.addQueue(5); //右边的图多了这个操作


代 码

package structure;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * 环形队列测试
 */



//支持,队列满了后,自动删除最老的数据
publicclassArrayQueueDemoV2 {
    publicstaticvoidmain(String[] args{
// List<Integer> list = new ArrayList<>(3);
// list.add(1);
// list.add(2);
// System.out.println("size:"+list.size());
// list.add(3);
// list.add(4);
//
// for (Integer i : list) {
// System.out.println(i);
// }
        test03();
    }
    publicstaticvoidtest03(){
        ArrayQueue arrayQueue = new ArrayQueue(6); //初始化一个队列
        int i = 0;
        while(i< 100){
            arrayQueue.addQueue(i++);
            arrayQueue.addQueue(i++);
            System.out.println("按照asc:");
            arrayQueue.showQueue();
            System.out.println("按照desc:");
            List<Integer> list = arrayQueue.showQueueByDesc(2);
            for (Integer integer : list) {
                System.out.println(integer);
            }

        }
    }


    publicstaticvoidtest02(){
        ArrayQueue arrayQueue = new ArrayQueue(6); //初始化一个队列
        int i = 0;
        while(i< 100){
            arrayQueue.addQueue(i++);
            arrayQueue.addQueue(i++);
            System.out.println("按照asc:");
            arrayQueue.showQueue();
            System.out.println("按照desc:");
            arrayQueue.showQueueByDesc();

        }
    }

    publicstaticvoidtest01(){
        ArrayQueue arrayQueue = new ArrayQueue(4); //初始化一个队列
        arrayQueue.addQueue(1);
        arrayQueue.addQueue(2);
        arrayQueue.addQueue(3);
        arrayQueue.addQueue(4);
        arrayQueue.addQueue(5);
// arrayQueue.addQueue(6);
        System.out.println("获取队头数据:"+arrayQueue.headQueue());
        System.out.println("打印队列当前数据:");
        arrayQueue.showQueue();

    }

    publicstaticvoidprintTest(){
        ArrayQueue arrayQueue = new ArrayQueue(3); //初始化一个队列
        char key = ' '//用于判断用户的操作
        Scanner scanner = new Scanner(System.in);

        boolean flag = true;
        while (flag) {
            System.out.println("s(show),显示队列");
            System.out.println("a(add),添加数据到队列");
            System.out.println("g(get),从队列取出数据");
            System.out.println("h(head),查看对头数据");
            System.out.println("e(exit),退出程序");
            key = scanner.next().charAt(0);
            switch (key) {
                case's':
                    arrayQueue.showQueue();
                    break;
                case'a':
                    System.out.println("请输入一个数字");
                    int val = scanner.nextInt();
                    try {
                        arrayQueue.addQueue(val); //如果队列中没有数据则会报错,所以用了个try,catch防止报错
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }

                    break;
                case'g':
                    try {
                        System.out.println("取出的数据是:" + arrayQueue.getQueue());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;

                case'h':
                    try { //如果队列中没有数据则会报错,所以用了个try,catch防止报错
                        System.out.println("队列头数据是:" + arrayQueue.headQueue());
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case'e':

                    flag = false;
                    scanner.close();
                    break;
            }
        }
        System.out.println("退出程序成功");
    }



    publicstaticclassArrayQueue {
        privateint maxSize; //表示数组最大容量
        privateint front; //队列头部
        privateint rear;//队列尾
        privateint[] arr; //模拟队列的数组

        publicArrayQueue(int maxSize{
            front = 0//指向队列元素第一位
            rear = 0//指向队列最后一个元素的后一位
            this.maxSize = maxSize;//赋值队列的最大容量
            arr = newint[maxSize];//初始化模拟队列的数组
        }

        //判断队列是否为满
        public boolean isFull({
            return (rear + 1) % maxSize == front; //这里环形数组会预留一个位置
// return rear % maxSize == front; 这里环形数组会预留一个位置
            // 所以maxsize为3的时候 只会存入2个数值
        }

        //判断队列是否为空
        public boolean isEmpty({
            return rear == front; //如果头指针等于尾指针则队列为空
        }

        //添加数据到队列
        publicvoidaddQueue(int num{
            // 判断队列是否满
            if (isFull()) {
                //todo 方案1:队列满了,不让再继续入队列
                //System.out.println("队列满,不能加入数据!!!");
                //todo 方案2:队列满了,自动清理,最早入队的那个数据
                System.out.println("队列满了,数据出队列:"+getQueue());
                //return;
            }
            //直接将数据加入
            arr[rear] = num;
            //将 rear 后移, 这里必须考虑取模,当rear指向最后一个位置的时候,
            // 加1则会重新标记为第一个位置,从而实现了环形
            rear = (rear + 1) % maxSize;
        }

        //数据出队列
        publicintgetQueue({
            //判断队列是否为空
            if (isEmpty()) {
                thrownew RuntimeException("队列为空!");
            }
            int val = arr[front]; //先把front指向的数据取出来
            front = (front + 1) % maxSize; //头指针后移
            return val; //返回数据
        }

        //显示队列内容,按照先后顺序打印
        publicvoidshowQueue({
            // 2 2+ 3
            for (int i = front; i < front + size(); i++) {
                // 2,3,4下表
                int index = i%maxSize;
                System.out.println(arr[index]);
            }
        }
        publicvoidshowQueueByDesc({
            // 2 2+ 3
            for (int i = front + size() -1; i >= front; i--) {
                // 4,3,2表
                int index = i%maxSize;
                System.out.println(arr[index]);
            }
        }
        public List<Integer> showQueueByDesc(int count{
            // 2 2+ 3
            List<Integer> list = new ArrayList<>(count);
            for (int i = front + size() -1; i >= front; i--) {
                // 4,3,2表
                int index = i%maxSize;
                //System.out.println(arr[index]);
                list.add(arr[index]);
                if(list.size()==count){
                    break;
                }
            }
            return list;
        }



        publicintsize({
            // 1+ 4 - 2 4 = 3
            return (rear + (maxSize) - front) % maxSize;//其实也是rear-front演化过来
            //小伙伴可以好好思考一下
        }

        //查看队列第一个元素
        publicintheadQueue({
            //判断队列是否为空
            if (isEmpty()) {
                thrownew RuntimeException("队列为空!");
            }
            return arr[front]; //返回数据
        }
    }
}




END


本文作者:田兆壮(上海新炬中北团队)

本文来源:“IT那活儿”公众号

文章转载自IT那活儿,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论