数据结构之队列

像栈一样,队列也是一种线性表。它允许在表的一端插入数据,在另一端删除元素。插入元素的这一端称之为队尾。删除元素的这一端我们称之为队首。队列的结构如下图所示。

队列的特性:

  • 在队尾插入元素,在队首删除元素。

  • FIFO(先进先出),就向排队取票一样。

数据结构之队列_第1张图片

顺序队列的实现

使用上一篇文章的动态数组实现了队列,代码非常简单,不多讲了。

public class ArrayQueue {

    private Array data;

    public ArrayQueue(){
        this.data = new DynamicArray();
    }

    public boolean isEmpty() {
        return data.isEmpty();
    }

    public int size() {
        return data.size();
    }

    public void enqueue(E e) {
        data.add(e);
    }

    public E dequeue() {
        return data.remove(0);
    }

    public E head() {
        return data.get(0);
    }

    @Override
    public String toString() {
        return "Head " + data.toString() + " Tail";
    }    
}

循环队列

从顺序队列的出队操作可以看出,当删除一个元素时,队列中的元素都要向前移动一个位置。为了解决这个问题,这里介绍下循环队列。

我们可以将队列看成一个首位相连的环。当一个元素从队列删除后,新空余出来的空间可以作为队列的尾部。如图下所示。

数据结构之队列_第2张图片

在编写代码之前,我们先考虑两个问题。队列的数组下标怎么计算?

如果是在普通的队列中,那么插入元素,直接将tail + 1即可。可是在循环队中,元素插入的位置可能是原来队首的位置,如上图中的I,这样如果直接用tail + 1就会造成越界。所以我们计算数组下标的时候要进行取模。使用( tail + 1 ) % n。同理,删除元素也是类似。

下标的问题解决了,那么还有个问题,如何判断队列是否为空,队列是否为满呢?

从上面的图可以看出当tail == head的时候,这个是否队列是即可以为空,又可以为满。那么我们不妨将队列最后一个元素的空气预留出来。这样的话当tail == head的时候,队列为空。当队列的tail + 1 == head,我们就认为队列已经满了,因为是循环队列,所以计算tail + 1 == head应该算上偏移量即 ( tail + 1 ) % n = head % n。

在下文的代码中,简单的用数组实现了循环队列。tail == head认为队列为空,( tail + 1 ) % n == head % n认为队列已满。另外队列支持扩容和缩容,扩容操作和缩容操作上一篇文章的动态数组一致,详见下文。

public class LoopQueue<E> {

    private E[] data;

    private int size;

    private int tail;

    private int head;

    public LoopQueue(){
        this(8);
    }

    public LoopQueue(int capcaity) {
        this.data = (E[]) new Object[capcaity + 1];
        this.head = 0;
        this.tail = 0;
        this.size = 0;
    }

    public int size() {
        return size;
    }

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

    public void enqueue(E e) {
        if((tail + 1) % data.length == head % data.length){
            resize(data.length * 2);
        }
        data[tail] = e;
        tail = (tail + 1)% data.length;
        size++;
    }

    public E dequeue() {
        if(tail == head){
            return null;
        }
        E e = data[head];
        head = (head + 1)% data.length;
        size--;
        if(size == data.length / 4 && data.length / 2 != 0){
            resize(data.length / 2);
        }
        return e;
    }

    public E head() {
        return data[head];
    }

    private void resize(int newCapcaity){
        E[] newData = (E[])new Object[newCapcaity + 1];
        for(int i = 0 ; i < size ; i ++) {
            newData[i] = data[(i + head) % data.length];
        }
        head = 0;
        tail = size;
        data = newData;
    }

    @Override
    public String toString() {
        StringBuilder sBuilder = new StringBuilder();
        sBuilder.append("Head ");
        for(int i = 0 ; i < size ; i ++) {
            sBuilder.append(data[(i + head) % data.length]).append(",");
        }
        int lastIndex = sBuilder.lastIndexOf(",");
        if(lastIndex != -1){
            sBuilder.deleteCharAt(lastIndex);
        }
        sBuilder.append(" Tail");
        return sBuilder.toString();
    }

你可能感兴趣的:(数据结构)