重学数据结构--队列

330 阅读4分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

本系列文章为个人学习总结,如果有发现错误或存在疑问之处,欢迎留言指点!

本文是重学数据结构系列的第三篇,系列文章如下:
1.算法时间复杂度和空间复杂度
2.重学数据结构--链表
3.重学数据结构--队列
4.重学数据结构--栈
5.重学数据结构--树

1.队列介绍

什么是队列?简单的理解,就是银行排队取钱,排在前面的先取钱,排在后面的后取钱。

  • 队列是一个有序列表,可以用数组或链表来实现(我们这里用数组实现)
  • 队列的特点:先进先出,先存入队列的数据,要先取出。后存入的要后取出

在数据结构中我们常说的队列有单向队列循环队列。下面我们用数组来模拟这两种队列。

2.单向队列

数组模拟思路

  • 队列本身是有序列表,若使用数组的结构来存储队列的数据,则队列数组的声明如下图,其中maxSize是该队列的最大容量。

  • 因为队列的输出、输入是分别从前后端来处理,因此需要两个变量front(头指针)及rear(尾指针)分别记录队列前后端的下标,front 会随着数据取出而改变,而rear则是随着数据取出而改变,如图所示:

  • 初始化

    初始化数组
    头指针下标==尾指针下标==-1(即队列没有任何元素)
    
  • 往队列添加数据

    
    1)判断队列是否满,如果满了则不能添加: 尾指针下标=最大容量-1  
    2)尾指针往后移一个位置:rear+1; arr[rear] = n
    
  • 从队列取出数据

    
    1)判断队列是否为空:头指针下标=尾指针下标
    2)front往后移动一个位置:front+1
    

实现

public class ArrayQueue {
    private int maxSize;  //数组的最大容量
    private int front;  // 队列头
    private int rear;   // 队列尾
    private int [] arr; // 用于存数据模拟队列
​
    /**
     * 初始化队列
     * @param maxSize
     */
    public ArrayQueue(int maxSize){
        this.maxSize = maxSize;
        arr = new int[maxSize];
        rear = -1;
        front = -1;
    }
​
​
    /**
     * 判断队列是否为空
     * @return
     */
    public boolean isEmpty(){
       return this.front == this.rear;
    }
​
    /**
     * 判断队列是否满
     * @return
     */
    public boolean isFull(){
        return this.rear == this.maxSize-1;
    }
​
​
    /**
     * 入队
     * @param value
     */
    public void addQueue(int value){
        //判断队列是否满
        if (isFull()){
            System.out.println("队列满,不能添加数据");
            return;
        }
        this.arr[++rear] = value;
    }
​
    /**
     * 出队
     */
    public int getQueue(){
        //判断是否为空
        if(isEmpty()){
            throw new RuntimeException("队列为空,没有数据可获取");
        }
        return arr[++front];
    }
​
    /**
     * 显示队列所有元素
     */
    public void showQueue(){
        if(isEmpty()){
            System.out.println("队列中没有元素");
            return;
        }
        for (int i = front+1; i <= rear; i++) {
            System.out.printf("arr[%d]=%d\n",i,arr[i]);
        }
    }
​
    /**
     * 显示队列头元素
     */
    public int showQueueHead(){
        //判断队列是否为空
        if(isEmpty()){
              throw new RuntimeException("队列为空,没有数据可获取");
        }
        return arr[front+1];
    }
}

测试


    public static void main(String[] args) {
        ArrayQueue queue = new ArrayQueue(4);
        queue.addQueue(1);
        queue.addQueue(3);
        queue.addQueue(4);
        queue.addQueue(5);
        queue.addQueue(6);
​
        System.out.println("显示队列中所有元素:");
        queue.showQueue();
​
        System.out.println("队列头部元素为"+queue.showQueueHead());
​
        System.out.println();
        System.out.println("出队元素为"+queue.getQueue());
        System.out.println("出队元素为"+queue.getQueue());
        System.out.println("出队元素为"+queue.getQueue());
​
    }

3.循环队列

对于普通队列而言,数组不能重复使用,这样就造成了空间浪费。怎样才能使队列能够重复利用呢?将队列改成一个环形的,这样就可以重复利用了。

思路

  • front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素front 的初始值 = 0
  • rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.rear 的初始值 = 0
  • 当队列满时,条件是 (rear + 1) % maxSize == front
  • 对队列为空的条件, rear == front
  • 当我们这样分析, 队列中有效的数据的个数(rear + maxSize - front) % maxSize
  • 我们就可以在单向的队列上修改得到,一个环形队列

实现


public class CircleQueue {
    private int maxSize;  //数组的最大容量
    private int rear;     //队列尾    rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
    private int front;    //队列头
    private int[] arr;    //用于存放数据,模拟队列
​
​
    /**
     * 初始化队列
     * @param maxSize
     */
    public CircleQueue(int maxSize){
        this.maxSize = maxSize;
        arr = new int[maxSize];
    }
​
    /**
     * 判断队列是否满
     * @return
     */
    public boolean isFull(){
        System.out.println("rear="+rear+"\tfront="+front);
        return (rear+1)%maxSize==front;
    }
​
    /**
     * 判断队列是否为空
     * @return
     */
    public boolean isEmpty(){
        return rear == front;
    }
​
    /**
     * 往队列添加数据
     * @param n
     */
    public void addQueue(int n){
        //判断是否满
        if (isFull()){
            System.out.println("队列满,不能添加数据");
            return;
        }
        arr[rear] = n;
        rear = (rear+1)%maxSize;
​
    }
​
    /**
     * 取出数据
     * @return
     */
    public int getQueue(){
        //判断是否为空
        if(isEmpty()){
            throw new RuntimeException("队列为空,没有数据可取");
        }
​
        int value = arr[front];
        front = (front+1)%maxSize;
        System.out.println("rear="+rear+"\tfront="+front);
        return value;
    }
​
    /**
     * 显示队列所有数据
     */
    public void showQueue(){
        //判断是否为空
        if(isEmpty()){
            System.out.println("队列为空,没有数据可显示");
            return;
        }
        for (int i = front; i < front + size(); i++) {
            System.out.printf("arr[%d]=%d\n",i%maxSize,arr[i%maxSize]);
        }
    }
​
    /**
     * 显示队头数据
     * @return
     */
    public int showQueueHead(){
        //判断是否为空
        if(isEmpty()){
            throw new RuntimeException("队列为空,没有数据可显示");
        }
        return arr[front];
    }
​
​
    /**
     *  求当前队列有效数个数
     */
    public int size(){
​
        return (rear-front+maxSize)%maxSize;
    }
}

测试


    public static void main(String[] args) {
        CircleQueue  queue = new CircleQueue(4);
        boolean loop = true;
        char key = ' '; //接受用户输入
        Scanner sc = new Scanner(System.in);
        while(loop){
            System.out.println("s(show)显示队列");
            System.out.println("g(get)从队列取出数据");
            System.out.println("a(add)添加数据");
            System.out.println("h(Head)显示队列头数据");
            System.out.println("e(exit)");
​
            key = sc.next().charAt(0); //接收一个字符串
            switch (key){
                case 's':
                    queue.showQueue();
                    break;
                case 'g':
                    try {
                        int value = queue.getQueue();
                        System.out.printf("取出的数据为%d\n",value);
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
​
                    break;
                case 'a':
                    System.out.println("输入你要添加的数");
                    int val = sc.nextInt();
                    queue.addQueue(val);
                    break;
                case 'h':
                    try {
                        int value = queue.showQueueHead();
                        System.out.printf("队列头部的数据为%d\n",value);
​
                    }catch (Exception e){
                        System.out.println(e.getMessage());
                    }
                    break;
                case 'e':
                    sc.close();
                    loop = false;
                    break;
​
                    default:
                        break;
            }
        }
    }

\