数据结构之队列

本文详细介绍了队列的概念、数组实现及环形队列的原理,通过实例展示了队列的添加、获取数据等操作,并讨论了队列满和空的状态处理,以及如何避免薛定谔队列问题。同时,提出了环形队列作为解决方案,允许队列空间重复使用。
摘要由CSDN通过智能技术生成

一、实际案例

按照任务产生的顺序完成它们的策略我们每天都会遇到:在商场门前排队的消费者、在收费站前排队的汽车或是计算机中等待处理的任务。任何服务型策略的基本原则就是公平。公平大多数人的想法应该优先服务等待最久的人,这正是先进先出(First In First Out.,FIFO)策略的准则。

二、基本介绍

2.1 队列定义

  1. 队列是一个有序列表,可以用数组或是链表来实现
  2. 遵循先进先出的原则,即先存储队列的数据要先被取出

2.2 图示

空队列:
在这里插入图片描述
进队列:
在这里插入图片描述
满队列
在这里插入图片描述
出队列
在这里插入图片描述

2.3 数组模拟

  1. 声明队列深度maxSize,即数据的长度
  2. 队列的输入输出完全依赖头(head)(tail)指针控制,其中tail随着数据入队列而增加,head随着数据出队列而增加。初始化head = tail = -1
  3. 队列空 head = tail
  4. 队列满tail = maxSize - 1

2.4 代码实现

package 队列;

/**
 * Project: data structure
 * Package: 队列
 * Version: 1.0
 * Author: wjun
 * <p>
 * Description:
 * Created by hc on 2021/01/28 11:28
 * © 1996 - 2021 Zhejiang Hong Cheng Computer Systems Co., Ltd.
 */
// 使用数组模拟队列
public class Queue {
    private final int maxSize;    //数组最大容量
    private final int[] queue;    //存储数据
    private int head;             //指向队列头
    private int tail;             //指向队列尾

    //创建队列
    public Queue(int maxSize) {
        this.maxSize = maxSize;
        queue = new int[maxSize];
        head = -1;
        tail = -1;
    }

    //判断队列是否满
    public boolean isFull() {
        return tail == maxSize - 1;
    }

    //判断队列是否空
    public boolean isEmpty() {
        return head == tail;
    }

    //添加数据,进队列
    public void add(int n) {
        //判断队列是否满
        if (isFull()) {
            System.out.println("队列满,无法添加数据");
            return;
        }
        tail++; //尾指针后移
        queue[tail] = n;
    }

    //获取数据,出队列
    public int get() {
        //判断队列书否空
        if (isEmpty()) {
            throw new RuntimeException("队列空,无法取出数据");
        }
        head++; //头指针后移
        return queue[head];
    }

    //显示队列所有数据
    public void show() {
        if (isEmpty()) {
            System.out.println("队列空,没有数据");
            return;
        }
        for (int i = 0; i < queue.length; i++) {
            System.out.printf("arr[%d] = %d\n", i, queue[i]);
        }
    }

    //显示队列头数据
    public int head() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,无法取出数据");
        }
        return queue[head + 1];
    }
}

演示代码:

package 队列;

import java.util.Scanner;

/**
 * Project: data structure
 * Package: 队列
 * Version: 1.0
 * Author: wjun
 * <p>
 * Description: 使用数组实现
 * Created by hc on 2021/01/28 10:57
 * © 1996 - 2021 Zhejiang Hong Cheng Computer Systems Co., Ltd.
 */
public class ArrayQueue {
    public static void main(String[] args) {
        Queue queue = new Queue(3);
        Scanner scanner = new Scanner(System.in);

        char key = ' ';//获取用户输入
        while (true) {
            System.out.println("s(show):显示队列  ");
            System.out.println("e(exit):退出程序  ");
            System.out.println("h(head):获取头数据");
            System.out.println("a(add): 添加数据  ");
            System.out.println("g(get): 获取数据  ");
            System.out.print(">> ");
            key = scanner.next().charAt(0);
            switch (key) {
                case 's':
                    queue.show();
                    break;
                case 'e':
                    System.exit(-1);
                    break;
                case 'h':
                    try {
                        int e1 = queue.head();
                        System.out.println("数据:" + e1);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'a':
                    System.out.print("输入一个数");
                    queue.add(scanner.nextInt());
                    break;
                case 'g':
                    try {
                        int e2 = queue.get();
                        System.out.println("数据:" + e2);
                    } catch (Exception e) {
                        System.out.println(e.getMessage());
                    }
                    break;
                default:
                    break;
            }
        }
    }
}
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> a
输入一个数10
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> a
输入一个数20
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> a
输入一个数30
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> a
输入一个数40
队列满,无法添加数据
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> g
数据:10
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> g
数据:20
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> g
数据:30
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> g
队列空,无法取出数据
s(show):显示队列  
e(exit):退出程序  
h(head):获取头数据
a(add): 添加数据  
g(get): 获取数据  
>> 

2.5 问题

  1. 队列无法重复使用,一次性队列,当tail = maxSize - 1宣告结束。
  2. 薛定谔队列:当不断的进队列使队列满,随后不断出队列使队列空,这个时候这个队列即空又满。

三、环形队列

环形队列可以解决上述问题,让队列空间可以重复使用。这就要求headtail可以循环取[0,maxSize]中的值,可以使用取模来实现。

3.1 图示

在这里插入图片描述

3.2 修正

  1. head指向队列第一个数据,tail指向队列最后一个数据的后一个,初始化head = tail = 0
  2. 队列空 tail = head
  3. 为避免出现薛定谔队列,需要预留一个空间,保证除初始化队列和队列空时两指针指向同一个位置外不会重合,因此队列满 (tail + 1) % head = maxSize

问题:如何确定队列中元素个数?
以上述环形队列为例,有如下情况maxSize = 8

headtailnum
011
176
305

可以看出规律
tail > head => num = tail - head
tail < head => num = maxSize - head
可以通过一个算式来统一num = (tail + maxSize - head) % maxSize

3.3 代码实现

package 队列;

/**
 * Project: data structure
 * Package: 队列
 * Version: 1.0
 * Author: wjun
 * <p>
 * Description: 环形队列
 * Created by hc on 2021/01/28 14:15
 * © 1996 - 2021 Zhejiang Hong Cheng Computer Systems Co., Ltd.
 */
/*
head 指向队列第一个元素 初始值0
tail 指向队列最后一个元素的后一个位置,初始值0
队列空:tail = head
队列满:(tail + 1) % maxSize = head
*/
public class CircularQueue {
    private final int maxSize;
    private final int[] queue;
    private int head;
    private int tail;

    public CircularQueue(int maxSize) {
        // 为什么+1?
        // 上述分析为避免薛定谔队列,需要预留一个空位
        // 因此在初始化数组是悄悄给既定大小加1
        // 即悄悄多创建一个弥补预留空位
        this.maxSize = maxSize + 1;
        head = 0;
        tail = 0;
        queue = new int[maxSize + 1];
    }

    // 判断队列是否满
    public boolean isFull() {
        return (tail + 1) % maxSize == head;
    }

    // 判断队列是否空
    public boolean isEmpty() {
        return tail == head;
    }

    // 添加数据,进队列
    public void add(int n) {
        if (isFull()) {
            System.out.println("队列满,无法添加数据");
            return;
        }
        queue[tail] = n;
        // 注意需要取模
        tail = (tail + 1) % maxSize;
    }

    // 获取数据,出队列
    public int get() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,无法取出数据");
        }
        int tmp = queue[head];
        // 注意需要取模
        head = (head + 1) % maxSize;
        return tmp;
    }

    // 显示队列所有数据
    public void show() {
        if (isEmpty()) {
            System.out.println("队列空,无法取出数据");
            return;
        }
        for (int i = head; i < head + size(); i++) {
            // 注意需要取模
            System.out.printf("queue[%d] = %d\n", i, queue[i % maxSize]);
        }
    }

    // 查看队列头
    public int head() {
        if (isEmpty()) {
            throw new RuntimeException("队列空,无法取出数据");
        }
        return queue[head];
    }

    // 获取队列元素个数
    private int size() {
        return (tail + maxSize - head) % maxSize;
    }
}

演示参照上面的代码…

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小王是个弟弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值