【Java数据结构与算法】Java如何实现环形队列

用Java如何实现环形队列

1. 数组模拟队列的问题

  • 数组使用一次就不能用,没有达到复用的效果。

2. 思路一

image-20220415150730550

  • 初始位置: frontrear 都在 -1。 front 指向队列第一个元素的前一个位置; rear 指向队列最后一个元素。
  • 队列元素个数记录:size ,用来判断队伍是否满或空。
  • 队列为空:当 size ==0 时,队列为空。
  • 队列已满:当 size >= maxSize 时,队列已满。
  • 添加数据临界条件:当 rear == maxSize -1 且队列没满时,将 rear 置为 -1,回到初始位置。
  • 弹出数据临界条件:当 front == maxSize -1 且队列没满时,将 front 置为 -1,回到初始位置。

思路一的特点就是:队头指针空,队尾指针不空。数组空间全部利用,没有一点浪费。并且我还添加了泛型结构,确保队列中的数据类型都是一致的。

3. 思路一代码实现

① 环形队列:

public class CircularQueue<T> {
    //属性
    private int front;//队头指针
    private int rear;//队尾指针
    private int maxSize;//队列最大容量
    private int size;//队列数据个数
    private Object[] queue;//队列数组

    //空参构造器
    public CircularQueue() {
        front = -1;
        rear = -1;
        maxSize = 16;
    }

    //指定容量构造器
    public CircularQueue(int maxSize) {
        this();
        this.maxSize = maxSize;
    }

    //获取队列数据个数
    public int getSize() {
        return size;
    }

    //添加数据
    public void add(T t) {
        //懒汉式:要添加数据了才创建数组
        if (queue == null) {
            queue = new Object[maxSize];
        }
        //判断队列是否已满
        if (size >= maxSize) {
            throw new RuntimeException("队列已满");
        }
        //走到这说明队列没满,考虑边界情况
        if (rear == maxSize - 1) {
            rear = -1;//循环到队伍开头,形成闭环
        }
        //真正添加数据的操作
        queue[++rear] = t;
        //数据个数加一
        size++;
    }

    //弹出数据
    public T pop() {
        //判断队列是否为空
        if (queue == null || queue.length == 0 || size == 0) {
            throw new RuntimeException("队列为空");
        }
        //边界情况
        if (front == maxSize - 1) {
            front = -1;
        }
        T t = (T) queue[++front];
        queue[front] = null;
        size--;
        return t;
    }

    //重写toString()
    @Override
    public String toString() {
        return "CircularQueue{" +
                "queue=" + Arrays.toString(queue) +
                '}';
    }
}

② 测试:

@Test
public void test1() {
    CircularQueue<Integer> queue = new CircularQueue<>(4);
    queue.add(1);
    queue.add(2);
    queue.add(3);
    queue.add(4);
    System.out.println(queue.getSize());
    System.out.println(queue);

    //出队测试
    queue.pop();
    queue.pop();
    System.out.println(queue.getSize());
    System.out.println(queue);

    //环形添加数据测试
    queue.add(5);
    queue.add(6);
    System.out.println(queue.getSize());
    System.out.println(queue);

    //环形弹出数据测试
    queue.pop();
    queue.pop();
    queue.pop();
    queue.pop();
    System.out.println(queue.getSize());
    System.out.println(queue);

    //再添加数据
    queue.add(7);
    queue.add(8);
    queue.add(9);
    queue.add(10);
    System.out.println(queue.getSize());
    System.out.println(queue);
}

输出:

4
CircularQueue{queue=[1, 2, 3, 4]}
2
CircularQueue{queue=[null, null, 3, 4]}
4
CircularQueue{queue=[5, 6, 3, 4]}
0
CircularQueue{queue=[null, null, null, null]}
4
CircularQueue{queue=[9, 10, 7, 8]}

4. 思路二

队列未满队列已满循环一次队列满的情形
image-20220521153251619image-20220521154147398image-20220521161528113
队列初始为空队列将要弹空
image-20220521155606513image-20220521160611069
  • 初始位置: frontrear 的初始值都等于 0 。 front 指向队列第一个元素; rear 指向队列最后一个元素的后一个位置。因为希望空出一个位置判断队列空满情况。
  • 队列元素个数:不用 size 记录数据个数,而是用公式 (rear + maxSize - front) % maxSize 计算得出。
  • 队列为空:当 front == rear 时,队列为空。
  • 队列已满:当 (rear+1) % maxSize == front 时 (图片下面的字错了,以这里为准),队列已满。这一条设计得非常精妙,取模 % 是一个哈希操作,能够把运算结果限定在 0 ~ maxSize - 1 的区间内。 非常适合限制队尾指针 rear 的范围,避免空指针异常。
  • 添加数据临界条件:当 rear == maxSize -1 且队列没满时,将 rear 置为 -1,回到初始位置。
  • 弹出数据临界条件:当 front == maxSize -1 且队列没满时,将 front 置为 -1,回到初始位置。

思路二的特点就是:队头指针不空,队尾指针为空。数组空间没有全部利用,只能利用 maxSize - 1 个位置。算法精华在于利用取模 % 运算限制指针的范围在数组内,避免空指针异常。

5. 思路二代码实现

① 环形队列:

public class CircularQueueTeacher<T> {
    //属性
    private int front;//队头指针
    private int rear;//队尾指针
    private int maxSize;//队列最大容量
    private Object[] queue;//队列数组

    //空参构造器
    public CircularQueueTeacher() {
        this.maxSize = 16;
    }

    //指定容量构造器
    public CircularQueueTeacher(int maxSize) {
        this();
        this.maxSize = maxSize;
    }

    //获取队列数据个数
    public int getSize() {
        return (rear + maxSize - front) % maxSize;
    }

    //添加数据
    public void add(T t) {
        //懒汉式:要添加数据了才创建数组
        if (queue == null) {
            queue = new Object[maxSize];
        }
        //判断队列是否已满
        if ((rear + 1) % maxSize == front) {
            throw new RuntimeException("队列已满");
        }
        //真正添加数据的操作
        queue[rear] = t;
        //边界条件,确保rear在数组范围内,并实现环形移动
        rear = (rear + 1) % maxSize;
    }

    //弹出数据
    public T pop() {
        //判断队列是否为空
        if (rear == front) {
            throw new RuntimeException("队列为空");
        }
        //真正弹出数据的操作
        T t = (T) queue[front];
        //将弹出位置置为空
        queue[front] = null;
        //边界条件,确保front在数组范围内,并实现环形移动
        front = (front + 1) % maxSize;
        //返回弹出数据
        return t;
    }

    //重写toString()方法

    @Override
    public String toString() {
        return "CircularQueueTeacher{" +
                "queue=" + Arrays.toString(queue) +
                '}';
    }
}

② 测试:

@Test
public void testTeacherCode() {
    CircularQueueTeacher<Integer> queue = new CircularQueueTeacher<>(4);
    //添加数据测试
    queue.add(1);
    queue.add(2);
    queue.add(3);
    System.out.println(queue.getSize());
    System.out.println(queue);

    //出队测试
    queue.pop();
    queue.pop();
    System.out.println(queue.getSize());
    System.out.println(queue);

    //环形添加数据测试
    queue.add(4);
    queue.add(5);
    System.out.println(queue.getSize());
    System.out.println(queue);

    //环形弹出数据测试
    queue.pop();
    System.out.println(queue.getSize());
    System.out.println(queue);

    //再添加数据
    queue.add(6);
    System.out.println(queue.getSize());
    System.out.println(queue);
}

输出:

3
CircularQueueTeacher{queue=[1, 2, 3, null]}
1
CircularQueueTeacher{queue=[null, null, 3, null]}
3
CircularQueueTeacher{queue=[5, null, 3, 4]}
2
CircularQueueTeacher{queue=[5, null, null, 4]}
3
CircularQueueTeacher{queue=[5, 6, null, 4]}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
做一门精致,全面详细的 java数据结构与算法!!!让天下没有难学的数据结构,让天下没有难学的算法,不吹不黑,我们的讲师及其敬业,可以看到课程视频,课件,代码的录制撰写,都是在深夜,如此用心,其心可鉴,他不掉头发,谁掉头发???总之你知道的,不知道的,我们都讲,并且持续更新,走过路过,不要错过,不敢说是史上最全的课程,怕违反广告法,总而言之,言而总之,这门课你值得拥有,好吃不贵,对于你知识的渴求,我们管够管饱话不多说,牛不多吹,我们要讲的本门课程内容:稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

自牧君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值