• 【数据结构】队列


    什么是队列?

    队列是一种线性数据结构,要理解它,其实非常简单,举个例子。

    假如高速公路上有一条隧道,所有通过隧道的车辆只允许从隧道的入口驶入,从隧道出口驶出,不允许逆行。因此,要想让车辆驶出隧道,只能按照车辆的驶入顺序,先驶入的车辆先驶出,后驶入的车辆后驶出,任何车辆都无法跳过它前面的车辆提前驶出。

    队列是一种线性数据结构,它不同于栈,队列中的元素只能先进先出(First In First Out,简称FIFO),队列的出口端叫队首,队列的入口段叫队尾。

    队列.png

    队列的实现

    1、入队(enqueue)

    入队就是把新的元素放入队列中,只允许从队尾放入元素,并且新元素将会成为下一个新的队尾。

    2、出队(dequeue)

    出队就是把队首元素移除队列,出队元素的后一个元素成为新队首。

    整体代码如下:

    /**
     * 描述:队列所需的方法。
     *
     * Create By ZhangBiao
     * 2020/5/10
     */
    public interface Queue<E> {
    
        /**
         * 入队操作
         *
         * @param e
         */
        void enqueue(E e);
    
        /**
         * 出队操作
         *
         * @return
         */
        E dequeue();
    
        /**
         * 获取队首
         *
         * @return
         */
        E getFront();
    
        /**
         * 获取队列元素个数
         *
         * @return
         */
        int getSize();
    
        /**
         * 判断队列是否为空
         *
         * @return
         */
        boolean isEmpty();
    
    }
    
    /**
     * 描述:基于动态数组实现队列。
     * <p>
     * Create By ZhangBiao
     * 2020/5/10
     */
    public class ArrayQueue<E> implements Queue<E> {
    
        private Array<E> array;
    
        public ArrayQueue() {
            array = new Array<>();
        }
    
        public ArrayQueue(int capacity) {
            array = new Array<>(capacity);
        }
    
        @Override
        public void enqueue(E e) {
            this.array.addLast(e);
        }
    
        @Override
        public E dequeue() {
            return this.array.removeFirst();
        }
    
        @Override
        public E getFront() {
            return this.array.getFirst();
        }
    
        @Override
        public int getSize() {
            return this.array.getSize();
        }
    
        @Override
        public boolean isEmpty() {
            return this.array.isEmpty();
        }
    
        /**
         * 获取队列容量大小。
         *
         * @return
         */
        public int getCapacity() {
            return this.array.getCapacity();
        }
    
        @Override
        public String toString() {
            StringBuilder result = new StringBuilder();
            result.append("Queue: ");
            result.append("front [");
            for (int i = 0; i < array.getSize(); i++) {
                result.append(array.get(i));
                if (i != array.getSize() - 1) {
                    result.append(", ");
                }
            }
            result.append("] tail");
            return result.toString();
        }
    }
    

    循环队列的实现

    注意:如果队列不断的出队,那么左边的内存空间就会浪费,这时就可以使用循环队列来维持队列容量。

    循环队列是什么呢?

    假设我们有一个这样的队列。如下:

    循环队列1.png

    这时执行一个入队操作,在数组不扩容的前提下,如何让新元素入队并确定新的队尾位置呢?可以利用已出队元素留下的空间,让队尾指针重新指向数组的首位。

    循环队列2.png

    这样一来,整个队列的元素就循环起来了。在物理存储上,队尾的位置可以在队首之前,当再有元素入队时,将其放入数组的首位,队尾指针后移即可。

    循环队列3.png

    一直到(tail + 1)% 数组长度 == front 时,代表队列已满。注意:队尾指针指向的位置永远空出一位,所以在循环队列中会浪费一个空间。

    循环队列4.png

    这就是所谓的循环队列。

    整体代码如下:

    /**
     * 描述:循环队列。
     * <p>
     * Create By ZhangBiao
     * 2020/5/10
     */
    public class LoopQueue<E> implements Queue<E> {
    
        private E[] data;
    
        private int front;
    
        private int tail;
    
        private int size;
    
        public LoopQueue() {
            this(10);
        }
    
        public LoopQueue(int capacity) {
            data = (E[]) new Object[capacity + 1];
            front = 0;
            tail = 0;
            size = 0;
        }
    
    
        @Override
        public void enqueue(E e) {
            if ((tail + 1) % data.length == front) {
                resize(getCapacity() * 2);
            }
            data[tail] = e;
            tail = (tail + 1) % data.length;
            size++;
        }
    
        /**
         * 扩容
         *
         * @param newCapactiy
         */
        private void resize(int newCapactiy) {
            E[] newData = (E[]) new Object[newCapactiy + 1];
            for (int i = 0; i < size; i++) {
                newData[i] = data[(front + i) % data.length];
            }
            data = newData;
            front = 0;
            tail = size;
        }
    
        @Override
        public E dequeue() {
            if (isEmpty()) {
                throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
            }
            E ret = data[front];
            data[front] = null;
            front = (front + 1) % data.length;
            size--;
            if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
                resize(getCapacity() / 2);
            }
            return ret;
        }
    
        @Override
        public E getFront() {
            if (isEmpty()) {
                throw new IllegalArgumentException("Queue is empty.");
            }
            return data[front];
        }
    
        @Override
        public int getSize() {
            return size;
        }
    
        @Override
        public boolean isEmpty() {
            return front == tail;
        }
    
        public int getCapacity() {
            return data.length - 1;
        }
    
        @Override
        public String toString() {
            StringBuilder result = new StringBuilder();
            result.append(String.format("Queue: size = %d , capacity = %d
    ", size, getCapacity()));
            result.append("front [");
            for (int i = front; i != tail; i = (i + 1) % data.length) {
                result.append(data[i]);
                if ((i + 1) % data.length != tail) {
                    result.append(", ");
                }
            }
            result.append("] tail");
            return result.toString();
        }
    
        public static void main(String[] args) {
            LoopQueue<Integer> queue = new LoopQueue<>();
            for (int i = 0; i < 10; i++) {
                queue.enqueue(i);
                System.out.println(queue);
                if (i % 3 == 2) {
                    queue.dequeue();
                    System.out.println(queue);
                }
            }
        }
    
    }
    
  • 相关阅读:
    从 java 代码到 android 进程的详细过程
    c++ 智能指针
    linux 进程间共享内存示例
    visual studio 在windows远程调试 linux 程序 cout 输出乱码
    wcf restful 访问报错 *.svc HTTP error 404.17
    c++ 创建 uuid guid
    c++ 事件回调 java
    java通过jna调用so
    java执行jar包
    java调用com组件com4j
  • 原文地址:https://www.cnblogs.com/zhangbiao97/p/12862588.html
Copyright © 2020-2023  润新知