4.讲究先来后到的队列

概述

目标:

  1. 队列的存储结构及操作特点
  2. java中队列相关的api
  3. 基于单链表的队列实现
  4. 刷题(设计循环队列)

存储结构及特点

队列(queue) 和栈一样,代表具有一类操作特征的数据结构,拿日常生活中的一个场景举例说明,去车站的窗口买票,就要排队,先来的人就先买,后到的人就后买,先来的人排至队头,后来的人排至队尾,不允许插队,先进先出,这就是典型的队列;先进先出(First In First Out) 即 FIFO,为了方便理解队列这种数据结构,将以两幅图,第一个是队列,第二个是栈,两图作对比
4.讲究先来后到的队列_第1张图片
4.讲究先来后到的队列_第2张图片
总结: 队列和栈一样都属于一种操作受限的线性表,栈只允许在一端进行操作,分别是入栈和出栈,而队列跟栈很相似,支持的操作也有限,最基本的两个操作,一个叫入队对,将数据插入到队列末尾,另一个叫出队列,从队列头部取出一个数据
注意: 入队列和出队列的时间复杂度均为O(1)

队列实现

java api

队列这种数据结构在高级语言中的实现特别的丰富,成熟
Interface Queue 链接
全类名 java.util.Queue

									方法摘要
Throws exception Returns special value
Insert add(e) offer(e)
Remove remove() poll()
Examine element() peek()

链表实现队列

基于单链表实现的队列,需要两个指针:head指针和 tail 指针;它们分别指向链表的第一个 节点 ,如图所示,入队时, tail.next = new_nodetail = tail.next 即先添加到链表尾,再将 tail 指向新加的 节点,出队时, head = head.next 即将原来的第一个头节点,变为原来的第二个节点
4.讲究先来后到的队列_第3张图片
实现时,将 java.util.Queue 直接复制过来
队列接口如下:

public interface Queue<E> {
    boolean add(E e);

    boolean offer(E e);

    E remove();

    E poll();

    E element();

    E peek();

    boolean isEmpty();
}

队列实现类如下:

public class LinkedListQueue<E> implements Queue<E> {
    // 队列大小
    int size;
    // 头节点
    Node<E> head;
    // 尾节点
    Node<E> tail;

    public LinkedListQueue() {
    }

    @Override
    public boolean add(E e) {
        addTail(e);
        return true;
    }

    @Override
    public boolean offer(E e) {
        addTail(e);
        return true;
    }

    @Override
    public E remove() {
        if (size == 0) {
            throw new NoSuchElementException("队列为空!");
        }
        return removeHead().val;
    }

    @Override
    public E poll() {
        if (size == 0) {
            return null;
        }
        return removeHead().val;
    }

    @Override
    public E element() {
        if (size == 0) {
            throw new NoSuchElementException("队列为空!");
        }
        return head.val;
    }

    @Override
    public E peek() {
        if (size == 0) {
            return null;
        }
        return head.val;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node<E> h = head;
        while (h != null) {
            sb.append(h.val).append("->");
            h = h.next;
        }
        return sb.append("null").toString();
    }

    public boolean isEmpty() {
        return size == 0;
    }

    private Node<E> removeHead() {
        Node<E> h = head;
        head = head.next;
        h.next = null;
        size--;
        return h;
    }

    private void addTail(E item) {
        Node<E> tmp = tail;
        Node<E> newNode = new Node<>(item, null);
        tail = newNode;
        if (tmp == null) {
            // 链表为空,即入队既是头又是尾
            head = newNode;
        } else {
            // tmp 此是是倒数第二个节点
            tmp.next = tail;
        }
        size++;
    }

    private static class Node<E> {
        E val;
        Node<E> next;

        public Node(E val, Node<E> next) {
            this.val = val;
            this.next = next;
        }
    }
}

测试类如下:

public static void main(String[] args) {
        Queue<String> queue = new LinkedListQueue();
        queue.add("1");
        queue.add("2");
        queue.add("3");
        System.out.println("队列是否为空:" + queue.isEmpty());
        System.out.println(queue);
        System.out.println("出队元素:"+queue.remove());
        System.out.println(queue);
        System.out.println("出队元素:"+queue.poll());
        System.out.println(queue);
        System.out.println("队列头元素:"+queue.peek());
        System.out.println(queue);
    }

刷题(设计循环队列)

设计循环队列
为充分利用队列空间,克服"假溢出"现象的方法是:将队列空间想象为一个首尾相接的圆环,循环队列(Circular Queue)是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列
4.讲究先来后到的队列_第4张图片

在循环队列中,当队列为空时,可知 front = rear;而当所有队列空间全占满时,也有 front = rear 。为了 区别 这两种情况,假设队列使用的数组有 capacity 个存储空间,则此时规定循环队列最多只能有 capacity - 1 个队列元素,当循环队列中只剩下一个空间存储单元时,则表示队列已满;根据以上可知,队列判空条件是 front = rear ,而队列判断为满的条件是 front = (rear + 1) mod capacity ,如上图队列已满。对于固定大小的数组,只要知道队尾 rear 与队首 front ,即可计算出队列当前的长度:(rear - front + capacity) mod capacity,如下图
4.讲究先来后到的队列_第5张图片

此时由公式:0 - 1 + 5 % 5 = 4 队列长度为 4

分析完来看代码

public class MyCircularQueue {

    private int[] data;
    private int front, rear;

    public MyCircularQueue(int k) {
        // 因为满队列实际上是数组少1个,所以加上1
        data = new int[k + 1];
    }

    public boolean enQueue(int value) {
        if (isFull()) {
            return false;
        }
        data[rear] = value;
        // 不能直接 rear ++ ,值会无限增大
        rear = getNext(rear);
        return true;
    }

    public boolean deQueue() {
        if (isEmpty()) {
            return false;
        }
        front = getNext(front);
        return true;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("头节点下标:" + front).append(",").append("尾节点下标:" + rear).append(" ");
        for (int i = 0; i < this.data.length; i++) {
            sb.append("a[").append(i).append("] = ").append(this.data[i]).append(",");
        }
        return sb.toString();
    }

    public boolean isEmpty() {
        return front == rear;
    }

    public boolean isFull() {
        return getNext(rear) == front;
    }

    private int getNext(int cur) {
        // 获取当前数据的下一个数据的下标
        return (cur + 1) % data.length;
    }
}

测试如下图,队列满,然后出一个进一个,重复两次执行
4.讲究先来后到的队列_第6张图片
测试

public class MyCircularQueueTest {
    public static void main(String[] args) {
        MyCircularQueue queue = new MyCircularQueue(5);
        System.out.println("队列是否为空:" + queue.isEmpty());
        queue.enQueue(1);
        System.out.println(queue);
        queue.enQueue(2);
        queue.enQueue(3);
        queue.enQueue(4);
        queue.enQueue(5);
        System.out.println(queue);
        System.out.println("加入是否成功!" + queue.enQueue(5));
        queue.deQueue();
        System.out.println("队列头部元素出队:" + queue);
        System.out.println("加入是否成功!" + queue.enQueue(6));
        System.out.println(queue);
        // -----
        queue.deQueue();
        System.out.println("队列头部元素出队:" + queue);
        System.out.println("加入是否成功!" + queue.enQueue(7));
        System.out.println(queue);

    }
}

结束

队列 至此就结束了,如有问题,欢迎评论区留言

你可能感兴趣的:(数据结构与算法,队列,queue,设计循环队列,leetcode,队列实现,java)