数据结构与算法--队列

简介

队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先被读出来。例如下图中进队列的顺序为1,2,3,4,则出队列的顺序为1,2,3,4

队列是一个有序列表,可以用数组或是链表来实现

 


数组实现队列

实现方式一:普通数组实现队列

分析

若使用数组的结构来存储队列的数据,则队列数组的声明如下图, 其中 maxSize 是该队列的最大容量。因为队列的输出、输入是分别从前后端来处理,因此需要两个变量 front 及 rear 分别记录队列前后端的下标,front 会随着数据输出而改变,而 rear 则是随着数据输入而改变

在数据入队列时需要判断队列是否已满,同时数据出队列时也要判断队列是否为空

  • 数据出队列时,若 rear == front 表示队列为空,无数据出队列
  • 数据入队列时,若 rear == maxSize - 1 表示队列已满,无法存入数据

该方式实现的缺陷:数组使用一次就不能使用了,没有达到复用的效果

API实现

使用普通数组实现队列数据的入队列、出队列、获取头部数据、提供forEach方式遍历

package Queue.ArrayQueue;

import java.util.Iterator;

public class ArrayQueue<T> implements Iterable<T>{

    /**表示数组最大容量*/
    private final int maxSize;
    
    /**队列尾*/
    private int rear;
    
    /**队列头*/
    private int front;
    
    /**存放数组元素,模拟数组队列*/
    private final T[] arr;
    
    /**数组中元素个数*/
    private int num;

    public ArrayQueue(int maxSize){
        this.maxSize = maxSize;

        //指向队列尾,指向队列尾的数据(即就是队列最后一个数据)
        this.rear = -1;

        //指向队列头部,分析出front是指向队列头的前一个位置
        this.front = -1;

        this.num = 0;

        arr = (T[]) new Object[maxSize];
    }

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

    /**判断队列是否空*/
    public boolean isEmpty(){
        return front == rear;
    }

    public int size(){
        return num;
    }

    /**添加数据到队列*/
    public void addQueue(T t){

        //判断队列是否满
        if(isFull()){
            System.out.println("队列已满");
            return;
        }

        arr[++rear] = t;
        num++;
    }

    /**获取队列的数据, 出队列*/
    public T delQueue(){

        if (isEmpty()) {
            System.out.println("队列已空");
            return null;
        }

        T data = arr[++front];
        arr[front] = null;
        num--;

        return data;
    }

    /**显示队列的对头数据, 注意不是取出数据*/
    public T getFirst(){

        if (isEmpty()) {
            System.out.println("队列已空");
            return null;
        }

        return arr[front+1];
    }

    @Override
    public Iterator<T> iterator() {

        //提供队列自定义的遍历方式
        return new MyIterator();
    }

    private class MyIterator implements Iterator<T>{

        public int index;

        public MyIterator(){
            this.index = 0;
        }

        @Override
        public boolean hasNext() {
            return index < maxSize;
        }

        @Override
        public T next() {
            return arr[index++];
        }
    }
}

测试

package Queue.ArrayQueue;

public class Test {
    public static void main(String[] args) {
        ArrayQueue<Integer> arrayQueue = new ArrayQueue<Integer>(10);
        arrayQueue.addQueue(1);
        arrayQueue.addQueue(2);
        arrayQueue.addQueue(3);
        arrayQueue.addQueue(4);
        arrayQueue.addQueue(5);
        arrayQueue.addQueue(6);

        for (Integer data : arrayQueue) {
            System.out.print(data+",");
        }
        System.out.println("-----------------------");
        System.out.println("队列中元素个数:"+arrayQueue.size());
        System.out.println("队头元素为:"+arrayQueue.getFirst());
        System.out.println("将队头元素拿出:"+arrayQueue.delQueue());
        System.out.println("队列中元素个数:"+arrayQueue.size());
        System.out.println("-----------------------");
        for (Integer data : arrayQueue) {
            System.out.print(data+",");
        }
    }
}

 结果如下

1,2,3,4,5,6,null,null,null,null,
-----------------------
队列中元素个数:6
队头元素为:1
将队头元素拿出:1
队列中元素个数:5
-----------------------
null,2,3,4,5,6,null,null,null,null,


实现方式二:循环数组实现队列

分析

可以根据实现方式一进行修改,将数组当做一个环形数组看待(通过取模的方式实现即可)

在数据入环形队列时需要判断队列是否已满,同时数据出环形队列时也要判断队列是否为空

  • 数据入环形队列时,将队列容量空出一个作为约定,尾索引的下一个为头索引时表示队列已满: (rear + 1) % maxSize == front 
  • 数据出环形队列时,当 rear == front 表示队列为空 

API实现

package Queue.ArrayQueue;

import java.util.Iterator;

public class CircleArrayQueue<T> implements Iterable<T>{

    /**表示数组最大容量*/
    private final int maxSize;

    /**队列尾*/
    private int rear;

    /**队列头*/
    private int front;

    /**存放数组元素,模拟数组队列*/
    private final T[] arr;

    /**数组中元素个数*/
    private int num;

    public CircleArrayQueue(int maxSize){

        this.maxSize = maxSize;

        //指向队列尾,指向队列尾的数据(即就是队列最后一个数据)
        this.rear = 0;

        //指向队列头部,分析出front是指向队列头的前一个位置
        this.front = 0;

        this.num = 0;

        arr = (T[]) new Object[maxSize];
    }

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

    /**判断队列是否空*/
    public boolean isEmpty(){
        return front == rear;
    }

    /**返回数组中元素的个数*/
    public int size(){
        return (rear + maxSize - front) % maxSize;
    }

    /**添加数据到队列*/
    public void addQueue(T t){

        //判断队列是否满
        if(isFull()){
            System.out.println("队列已满");
            return;
        }

        arr[rear] = t;
        //将 rear 后移, 这里必须考虑取模
        rear = (rear + 1) % maxSize;
        num++;
    }

    /**获取队列的数据, 出队列*/
    public T delQueue(){

        if (isEmpty()) {
            System.out.println("队列已空");
            return null;
        }

        // 这里需要分析出 front是指向队列的第一个元素
        // 1. 先把 front 对应的值保留到一个临时变量
        // 2. 将 front 后移, 考虑取模
        // 3. 将临时保存的变量返回
        T data = arr[front];
        front = (front + 1) % maxSize;
        num--;

        return data;
    }

    /**显示队列的对头数据, 注意不是取出数据*/
    public T getFirst(){

        if (isEmpty()) {
            System.out.println("队列已空");
            return null;
        }

        return arr[front];
    }

    /**显示队列的所有数据*/
    public void showQueue() {

        if (isEmpty()) {
            System.out.println("队列空的,没有数据");
            return;
        }
        // 思路:从front开始遍历
        for (int i = front; i < front + size() ; i++) {
            int num = i % maxSize;
            System.out.printf("arr[%d]=%d\n", num, arr[num]);
        }
    }

    /**提供遍历数组的方法*/
    @Override
    public Iterator<T> iterator() {

        return new MyIterator();
    }

    private class MyIterator implements Iterator<T>{

        public int index;

        public MyIterator(){
            this.index = 0;
        }

        @Override
        public boolean hasNext() {
            return index < maxSize;
        }

        @Override
        public T next() {
            return arr[index++];
        }
    }
}

测试

package Queue.ArrayQueue;

public class TestCircleArrayQueue {
    public static void main(String[] args) {
        CircleArrayQueue<Integer> circleArrayQueue = new CircleArrayQueue<>(10);

        circleArrayQueue.addQueue(1);
        circleArrayQueue.addQueue(2);
        circleArrayQueue.addQueue(3);
        circleArrayQueue.addQueue(4);
        circleArrayQueue.addQueue(5);
        circleArrayQueue.addQueue(6);
        circleArrayQueue.addQueue(7);
        circleArrayQueue.addQueue(8);
        circleArrayQueue.addQueue(9);
        circleArrayQueue.addQueue(10);

        for (Integer data : circleArrayQueue) {
            System.out.print(data+",");
        }

        System.out.println();
        System.out.println("----------circleArrayQueue-------------");
        System.out.println("队列中元素个数:"+circleArrayQueue.size());
        System.out.println("队头元素为:"+circleArrayQueue.getFirst());
        System.out.println("将队头元素拿出:"+circleArrayQueue.delQueue());
        System.out.println("队列中元素个数:"+circleArrayQueue.size());
        System.out.println("-----------------------");
        for (Integer data : circleArrayQueue) {
            System.out.print(data+",");
        }
    }
}

结果如下

队列已满
1,2,3,4,5,6,7,8,9,null,
----------circleArrayQueue-------------
队列中元素个数:9
队头元素为:1
将队头元素拿出:1
队列中元素个数:8
----------circleArrayQueue-------------
1,2,3,4,5,6,7,8,9,null,


单链表实现队列

分析

链表是一种物理存储单元上非连续、非顺序的存储结构,其物理结构不能表示数据元素的逻辑顺序,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成,结点可以在运行时动态生成。所以可以通过链表的头结点实现出队列以及链表的尾结点实现入队列的操作。

实现方式:可以设计一个类,来描述结点这个事物,用一个属性描述这个结点存储的元素,用来另外一个属性描述这个结点的下一个结点

API实现

使用单链表实现队列数据的入队列、出队列、提供forEach循环遍历、获取队头数据

节点类

package Queue.LinearQueue;

public class Node<T>{

    public T item;
    public Node next;

    public Node(T item,Node next){
        this.item = item;
        this.next = next;
    }
}

链表队列实现类

package Queue.LinearQueue;

import java.util.Iterator;

public class LinearQueue<T> implements Iterable<T>{

    /**记录首结点*/
    private final Node<T> head;

    /**当前队列中元素个数*/
    private int n;

    /**记录最后一个结点*/
    private Node<T> last;

    public LinearQueue(){
        this.head = new Node<>(null,null);
        this.last = null;
        this.n = 0;
    }

    /**判断队列是否为空,是返回true,否返回false*/
    public boolean isEmpty(){
        return n == 0;
    }

    /**获取队列中元素的个数*/
    public int size(){
        return n;
    }

    /**往队列种插入一个元素*/
    public void enQueue(T t){

        if(null == last){
            last = new Node<>(t, null);
            head.next = last;
        }else{
            Node<T> oldLast = last;
            this.last = new Node<>(t, null);
            oldLast.next = last;
        }
        n++;
    }


    /**从队列中拿出一个元素*/
    public T deQueue(){
        if(isEmpty()){
            return null;
        }

        Node<T> oldFirst = head.next;
        head.next = oldFirst.next;
        n--;

        //如果队列已经删除完了,需要将last重置为null
        if(isEmpty()){
            this.last = null;
        }

        return (T)oldFirst.item;
    }

    /**提供外部遍历方式*/
    @Override
    public Iterator<T> iterator(){
        return new MyIterator();
    }

    private class MyIterator implements Iterator<T>{

        public Node<T> node;

        public MyIterator(){
            this.node = head;
        }

        @Override
        public boolean hasNext(){

            return node.next != null;
        }

        @Override
        public T next(){

            node = node.next;
            return node.item;
        }
    }
}

测试

package Queue.LinearQueue;

public class Test{
    public static void main(String[] args){

        //创建Queue对象
        LinearQueue<String> linearQueue = new LinearQueue<String>();
        //添加元素
        linearQueue.enQueue("a");
        linearQueue.enQueue("b");
        linearQueue.enQueue("c");
        linearQueue.enQueue("d");
        linearQueue.enQueue("e");

        System.out.println("------------------原队列数据-------------------");
        for(String value : linearQueue){
            System.out.print(value+",");
        }

        System.out.println();
        System.out.println("----------------队列大小-----------------------");
        System.out.println(""+ linearQueue.size());
        System.out.println("----------------移除队列的元素------------------");
        String result = linearQueue.deQueue();
        System.out.println(result);
        System.out.println("----------------返回队列的元素个数---------------");
        System.out.println(linearQueue.size());
        System.out.println("------------------队列数据---------------------");
        for(String value : linearQueue){
            System.out.print(value+",");
        }
    }
}

结果

------------------原队列数据-------------------
a,b,c,d,e,
----------------队列大小-----------------------
5
----------------移除队列的元素------------------
a
----------------返回队列的元素个数---------------
4
------------------队列数据---------------------
b,c,d,e,

 

posted @ 2022-07-17 16:17  伊文小哥  阅读(85)  评论(0编辑  收藏  举报