美文网首页
数据结构-队列

数据结构-队列

作者: 鼬殿 | 来源:发表于2020-05-19 17:13 被阅读0次

    队列是一种特殊的线性表,只能在头尾两端进行操作

    • 队尾(rear):只能从队尾添加元素,一般叫做 enQueue,入队
    • 队头(front):只能从队头移除元素,一般叫做 deQueue,出队
    • 先进先出的原则,First In First OutFIFO

    队列的接口设计

    int size(); // 元素的数量
    boolean isEmpty();//是否为空
    void clear();// 清空
    void enQueue(E element);//入队
    E deQueue();// 出队
    E front();// 获取队列的头元素

    队列的内部实现可以直接利用前面章节提到的数据结构(动态数组、链表)

    优先使用双向链表,因为队列主要是往头尾操作元素

    package com.njf;
    
    public class Queue<E> {
        
        private DoublyLinkedList<E> list = new DoublyLinkedList<>();
        
        public int size() {
            return list.size();
        }
    
        public boolean isEmpty() {
            return list.isEmpty();
        }
        
        public void clear() {
            list.clear();
        }
    
        public void enQueue(E element) {
            list.add(element);
        }
    
        public E deQueue() {
            return list.remove(0);
        }
    
        public E front() {
            return list.get(0);
        }
    }
    

    代码调用

    package com.njf;
    
    public class Main {
    
        public static void main(String[] args) {
            Queue<Integer> queue = new Queue<>();
            queue.enQueue(11);
            queue.enQueue(22);
            queue.enQueue(33);
            queue.enQueue(44);
            
            while (!queue.isEmpty()) {
                System.out.println(queue.deQueue());
            }
    
        }
    }
    

    出队结果

    11
    22
    33
    44
    

    双端队列(Deque)

    双端队列是能在头尾两端添加、删除的队列,英文 dequedouble ended queue 的简称

    接口设计

    int size();// 元素的数量
    boolean isEmpty(); // 是否为空
    void clear();//清空
    void enQueueRear(E element);// 从队尾入队
    E deQueueFront(); // 从队头出队
    void enQueueFront(E element);// 从队头入队
    E deQueueRear(); // 从队尾出队
    E front(); // 获取队列的头元素
    E rear();// 获取队列的尾元素

    代码实现

    package com.njf;
    
    public class Deque<E>  {
        private DoublyLinkedList<E> list = new DoublyLinkedList<>();
        
        public int size() {
            return list.size();
        }
    
        public boolean isEmpty() {
            return list.isEmpty();
        }
        
        public void clear() {
            list.clear();
        }
    
        public void enQueueRear(E element) {
            list.add(element);
        }
    
        public E deQueueFront() {
            return list.remove(0);
        }
    
        public void enQueueFront(E element) {
            list.add(0, element);
        }
    
        public E deQueueRear() {
            return list.remove(list.size() - 1);
        }
    
        public E front() {
            return list.get(0);
        }
    
        public E rear() {
            return list.get(list.size() - 1);
        }
    }
    

    代码调用

    package com.njf;
    
    public class Main {
    
        public static void main(String[] args) {
            Deque<Integer> queue = new Deque<>();
            queue.enQueueFront(11);
            queue.enQueueFront(22);
            queue.enQueueRear(33);
            queue.enQueueRear(44);
            /* 尾  44  33   11  22 头 */
            while (!queue.isEmpty()) {
                System.out.println(queue.deQueueRear());
            }
        }
    }
    

    队尾出队结果

    44
    33
    11
    22
    

    循环队列(Circle Queue)

    ◼ 其实队列底层也可以使用动态数组实现,并且各项接口也可以优化到 O(1) 的时间复杂度
    ◼ 这个用数组实现并且优化之后的队列也叫做:循环队列

    front指针的改变只针对:队头的操作(在队头添加和删除元素)

    代码实现

    package com.njf;
    @SuppressWarnings("unchecked")
    
    public class CircleQueue<E> {
        private E[] elements;
        private int size;
        private int front;
        private static final int DEFAULT_CAPACITY = 10;
        
        public CircleQueue() {
            elements = (E[]) new Object[DEFAULT_CAPACITY];
        }
        
        public int size() {
            return size;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
        
        public void clear() {
            for (int i = 0; i < size; i++) {
                elements[index(i)] = null;
            }
            front = 0;
            size = 0;
        }
    
        public void enQueue(E element) {
            ensureCapacity(size + 1);
            elements[index(size)] = element;
            size ++;
        }
    
        public E deQueue() {
            E frontElement = elements[front];
            elements[front] = null;
            front = index(1);
            size --;
            return frontElement;
        }
    
        public E front() {
            return elements[front];
        }
        
        /**
         * 保证要有capacity的容量
         * @param capacity
         */
        private void ensureCapacity(int capacity) {
            int oldCapacity = elements.length;
            if (oldCapacity >= capacity) return;
            // 新容量为旧容量的1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            E[] newElements = (E[]) new Object[newCapacity];
            for (int i = 0; i < size; i++) {
                newElements[i] = elements[index(i)];
            }
            elements = newElements;
            // 重置front
            front = 0;
        }
        
        private int index(int index) {
            return (front + index)%elements.length;
        }
        
        @Override
        public String toString() {
            StringBuilder string = new StringBuilder();
            string.append("capcacity=").append(elements.length)
            .append(" size=").append(size)
            .append(" front=").append(front)
            .append(", [");
            for (int i = 0; i < elements.length; i++) {
                if (i != 0) {
                    string.append(", ");
                }
                string.append(elements[i]);
            }
            string.append("]");
            return string.toString();
        }
    }
    

    代码调用

    package com.njf;
    
    public class Main {
    
        public static void main(String[] args) {
            test();
        }
        static void test() {
            CircleQueue<Integer> queue = new CircleQueue<Integer>();
            // 0 1 2 3 4 5 6 7 8 9
            for (int i = 0; i < 10; i++) {
                queue.enQueue(i);
            }
            // null null null null null 5 6 7 8 9
            for (int i = 0; i < 5; i++) {
                queue.deQueue();
            }
            // 15 16 17 18 19 5 6 7 8 9
            for (int i = 15; i < 25; i++) {
                queue.enQueue(i);
            }
            System.out.println(queue);
            queue.clear();
            System.out.println(queue);
        }
    }
    

    显示结果

    capcacity=15 size=15 front=0, [5, 6, 7, 8, 9, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
    capcacity=15 size=0 front=0, [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
    

    循环双端队列

    代码实现

    package com.njf;
    @SuppressWarnings("unchecked")
    
    public class CircleDeque<E> {
        private E[] elements;
        private int size;
        private int front;
        private static final int DEFAULT_CAPACITY = 10;
        
        public CircleDeque() {
            elements = (E[]) new Object[DEFAULT_CAPACITY];
        }
        
        public int size() {
            return size;
        }
    
        public boolean isEmpty() {
            return size == 0;
        }
        
        public void clear() {
            for (int i = 0; i < size; i++) {
                elements[index(i)] = null;
            }
            front = 0;
            size = 0;
        }
    
        /**
         * 从尾部入队
         * @param element
         */
        public void enQueueRear(E element) {
            ensureCapacity(size + 1);
            elements[index(size)] = element;
            size ++;
        }
    
        /**
         * 从头部出队
         * @param element
         */
        public E deQueueFront() {
            E frontElement = elements[front];
            elements[front] = null;
            front = index(1);
            size --;
            return frontElement;
        }
    
        /**
         * 从头部入队
         * @param element
         */
        public void enQueueFront(E element) {
            ensureCapacity(size + 1);
            front = index(-1);
            elements[front] = element;
            size ++;
        }
    
        /**
         * 从尾部出队
         * @param element
         */
        public E deQueueRear() {
            int rearIndex = index(size - 1);
            E rear = elements[rearIndex];
            elements[rearIndex] = null;
            size --;
            return rear;
        }
    
        public E front() {
            return elements[front];
        }
    
        public E rear() {
            return elements[index(size -1)];
        }
        /**
         * 保证要有capacity的容量
         * @param capacity
         */
        private void ensureCapacity(int capacity) {
            int oldCapacity = elements.length;
            if (oldCapacity >= capacity) return;
            // 新容量为旧容量的1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            E[] newElements = (E[]) new Object[newCapacity];
            for (int i = 0; i < size; i++) {
                newElements[i] = elements[index(i)];
            }
            elements = newElements;
            // 重置front
            front = 0;
        }
        
        private int index(int index) {
            index += front;
            if (index < 0) {
                return index + elements.length;
            }
            return index%elements.length;
        }
        
        @Override
        public String toString() {
            StringBuilder string = new StringBuilder();
            string.append("capcacity=").append(elements.length)
            .append(" size=").append(size)
            .append(" front=").append(front)
            .append(", [");
            for (int i = 0; i < elements.length; i++) {
                if (i != 0) {
                    string.append(", ");
                }
                string.append(elements[i]);
            }
            string.append("]");
            return string.toString();
        }
    }
    

    代码调用

    package com.njf;
    
    public class Main {
    
        public static void main(String[] args) {
            test();
        }
        static void test() {
            CircleDeque<Integer> queue = new CircleDeque<>();
            // 头5 4 3 2 1  100 101 102 103 104 105 106 8 7 6 尾
            // 头 8 7 6  5 4 3 2 1  100 101 102 103 104 105 106 107 108 109 null null 10 9 尾
            for (int i = 0; i < 10; i++) {
                queue.enQueueFront(i + 1);
                queue.enQueueRear(i + 100);
            }
            // 头 null 7 6  5 4 3 2 1  100 101 102 103 104 105 106 null null null null null null null 尾
            for (int i = 0; i < 3; i++) {
                queue.deQueueFront();
                queue.deQueueRear();
            }
            // 头 11 7 6  5 4 3 2 1  100 101 102 103 104 105 106 null null null null null null 12 尾
            queue.enQueueFront(11);
            queue.enQueueFront(12);
            System.out.println(queue);
            while (!queue.isEmpty()) {
                System.out.println(queue.deQueueFront());
            }
        }
    }
    

    显示结果

    capcacity=22 size=16 front=21, [11, 7, 6, 5, 4, 3, 2, 1, 100, 101, 102, 103, 104, 105, 106, null, null, null, null, null, null, 12]
    12
    11
    7
    6
    5
    4
    3
    2
    1
    100
    101
    102
    103
    104
    105
    106
    

    %运算符优化

    尽量避免使用乘*、除/、模%、浮点数运算,效率低下

     private int index(int index) {
            return (front + index)%elements.length;
        }
    

    优化

    private int index(int index) {
        front += index;
        return index - (index >= elements.length ? elements.length : 0);
    }
    

    已知n>=0,m>0
    n%m 等价于 n – (m > n ? 0 : m) 的前提条件:n < 2m

    相关文章

      网友评论

          本文标题:数据结构-队列

          本文链接:https://www.haomeiwen.com/subject/utpuohtx.html