【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]}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

卡皮巴拉不躺平

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

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

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

打赏作者

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

抵扣说明:

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

余额充值