队列顺序存储及循环队列

       队列,它也是一种线性结构,它 和栈的不同之处,就是队列只能从一端(队尾)添加元素,只能从另一端取出元素。队列其实就是类似我们生活中的排队一样(但不允许插队)。

       队列是一种先进先出(First In First Out)的数据结构,简称FIFO,允许插入的一端被称为队尾,,允许删除的一端称为队首。

       数组队列的操作

package com.hqw.queue;

/**
 * @author hqw521@qq.com
 * @date 2018/6/5 11:02
 */
public interface Queue<E> {
    int getSize();
    boolean isEmpty();
    void enqueue(E e);
    E dequeue();
    E getFront();
}

       数组队列的操作实现

package com.hqw.queue;

import com.hqw.array.Array;

/**
 * @author hqw521@qq.com
 * @date 2018/6/5 11:03
 */
public class ArrayQueue<E> implements Queue<E> {
    private Array<E> array;
    public ArrayQueue(int capacity) {
        array = new Array<>(capacity);
    }

    public ArrayQueue() {
        array = new Array<>();
    }

    @Override
    public int getSize() {
        return array.getSize();
    }

    @Override
    public boolean isEmpty() {
        return array.isEmpty();
    }

    @Override
    public void enqueue(E e) {
        array.addLast(e);
    }

    @Override
    public E dequeue() {
        return array.removeFirst();
    }

    @Override
    public E getFront() {
        return array.getFirst();
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("Queue: front[");
        for (int i = 0; i < getSize(); i++) {
            res.append(array.get(i));
            if (i != getSize() - 1) {
                res.append(", ");
            }
        }
        res.append("]tail");
        return res.toString();
    }
}

       所谓的入队列操作,其实就是在队尾追加一个元素,不需要移动任何元素,因此时间复杂度为O(1),而与栈不同的是,队列元素的出列是在队头,即下标为0的位置,那也就意味着,对列中的所有元素都需要向前移动,以保证队列的队头,也就是下标为0的位置不为空,此时时间复杂度为O(n)。

        但是想想,出队列时,不一定要全部移动,也就是说,队头不需要一定在下标为0的位置。如下图所示

       

       在循环队列中,我们又引入了两个元素,front和tail,front指向队头元素,tail指向队尾元素。当front == tail时,队列为空。

       当指针指向如下图所示时

    

就会出现“假溢出”现象,而解决假溢出的办法就是后面满了,就在从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。

     刚才前面提到front == tail时,队列为空,但在这种循环队列中,队列满时,front 与tail也是相等的。为了解决这种问题,我们需要修改其条件,保留一个元素空间。也就是说,队列满时,数组中其实还有一个空闲的单元。如下图所示的两种情况:


在这时,我们就说队列已经满了。队列的最大尺寸为capacity,队列满的条件是(tail + 1) % capacity == front(取摸“%”的  目的是为了解决tail与front大小的问题)。通用的计算队列长度公式为:(tail - front + capacity)% capacity。在这里我就不解释这两个公式,有兴趣的朋友可以去了解一下。

实现循环队列的代码。

package com.hqw.queue;

/**
 * @author hqw521@qq.com
 * @date 2018/6/5 19:45
 */
public class LoopQueue<E> implements Queue<E> {
    private E[] data;
    private int front;
    private int tail;

    public LoopQueue(int capactiy) {
        data = (E[]) new Object[capactiy + 1];
        front = 0;
        tail = 0;
    }

    public LoopQueue() {
        this(10);
    }

    /**
     * 获取队列大小
     * @return
     */
    public int getCapacity() {
        return data.length - 1;
    }

    /**
     * 获取队列长度
     * @return
     */
    @Override
    public int getSize() {
        return (tail - front + data.length) % data.length;
    }

    /**
     * 查看队列是否为空
     * @return
     */
    @Override
    public boolean isEmpty() {
        return front == tail;
    }

    /**
     * 入队
     * @param value
     */
    @Override
    public void enqueue(E value) {
        if ((tail + 1) % data.length == front) {
            resize(getCapacity() * 2);
        }
        data[tail] = value;
        tail = (tail + 1) % data.length;
    }

    /**
     * 出队列
     * @return
     */
    @Override
    public E dequeue() {
        if (isEmpty()) {
            throw new IllegalArgumentException("Queue is empty, dequeue failed");
        }
        E temp = data[front];
        front = (front + 1) % data.length;
        if (getSize() <= getCapacity() / 4 && getCapacity() / 2 != 0) {
            resize(getCapacity() / 2);
        }
        return  temp;
    }

    /**
     * 获取队首元素
     * @return
     */
    @Override
    public E getFront() {
        if (isEmpty()) {
            throw new IllegalArgumentException("Queue is empty");
        }
        return data[front];
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(String.format("Queue: size = %d, capactiy = %d\n", getSize(), getCapacity()));
        stringBuilder.append("front [");
        for (int i = front; i != tail; i = (i + 1) % data.length) {
            stringBuilder.append(data[i]);
            if ((i + 1) % data.length != tail) {
                stringBuilder.append(", ");
            }
        }
        stringBuilder.append("]tail");
        return stringBuilder.toString();
    }

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值