数据结构之队列QUEUE

队列是一种常见的数据结构。队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

上述内容取自于百度百科,也简单的介绍了下队列是什么样的一种结构。这种线性表的规则就是FIFO(First in first out),谁是第一个进去的,谁就第一个出来。这里可能把他想象成一根直的管道。左边是出口,只能出,右边是入口,只能进入。这就是一个简单的队列模型。当然,其实队列是基于双向链表来实现的,只是它在双向链表的基础上,进行了一些限定。一端不允许进入,一端不允许输出。下面就是简单的一个队列的图。
首先是队列的图形:
(正常的队列
形象得看出,出口方向在左边,出口则是在右边。进栈顺序则是,12345,那么front是什么意思呢,代表的是将要出列的元素的位置,而rear就是下一个要插入元素的位置。那么来插入一个元素:
在这里插入图片描述
插入一个6之后,rear增加到下一个位置,而没有执行出列操作,front不变。接着继续执行出列操作。
在这里插入图片描述
这里我们可以看到,出列后,rear指向的位置后移了一个,而这个操作适合front无关的。这边是队列queue这种数据结构的特征。而我们通过搜索可以发现,queue在java中只是一个借口,而下面也没有具体的实现类。这点与stack是不同的,那么我们便需要自己来实现这个接口,同时来复写这个接口的方法。

第一种方法便是使用我们已经写好的Array,也就是增强数组,作为底层的结构。代码如下:

package com.it.data;


public class ArrayQueue<E>
{
    private Array<E> queue;
    public ArrayQueue(){
        queue = new Array<>();
    }

    public ArrayQueue(int capacity){
        queue = new Array<>(capacity);
    }

    public boolean offer(E e){
        queue.addLast(e);
        return true;
    }

    public E poll(){
        return queue.delFirst();
    }

    public E peek(){
        return queue.get(queue.getSize()-1);
    }

    public int getSize(){
        return queue.getSize();
    }

    public int getCapacity() {
        return queue.getCapacity();
    }
    public static void main(String args[]){
        ArrayQueue arr = new ArrayQueue(3);
        arr.offer(1);
        arr.offer(2);
        arr.offer(3);
        arr.offer(4);
        arr.offer(5);
        System.out.println(arr.getCapacity());
        int j = arr.getSize();
        for (int i = 0; i<j; i++) {
            System.out.println(arr.poll());
        }
        //会抛出runtime异常,因为此时数组已经空了。
        arr.poll();
    }
}

这个代码就刨除了front以及rear这两个指针变量,那么,他没有这两个指针,如何确定增加以及删除的位置呢?
过程

那么从时间复杂度上来说,每次增加元素都为O(1),但是单次删除元素的操作就为O(n),如果这个链表大小为上千甚至上亿,那么这个时间就有些吓人了。

所以我们需要将删除的这个操作优化一下,使用链式的队列来完成。这时候我们就不能在调用自己写的Array数组了,因为Array数组中的delFirst操作,就是将每个数组的位置来进行移动。

 public void addLast(T t){
        add(size,t);
    }

 public void add(int index,T t){
        if(index>size || index<0)
            throw new IllegalArgumentException("插入角标违法,请检查后再输入");

        if (this.getCapacity() == this.getSize()) {
            capactyInc(this.getCapacity()*2);
        }
        for (int i = index; i < size ; i++) {
            data[i+1]  = data[i];
        }
        data[index] = t;
        size++;
    } 

接下来我们直接使用一维数组,来进行链式的队列操作。这样我们在移除元素时,就不需要再将每个元素向前移动一维。具体的示意图如下:

在这里插入图片描述
当然这样的节奏也有一些问题,首先,当数组满的时候,rear会重新到数组头,呢么,rear和front是相等的,可是这个时候,队列真的已经满了吗,那可不一定,在我们刚创立数组的时候,也会出现front和rear同时指向一个地址的情况,这个时候,便可以空出一个位置,来进行判断,也就是说,当rear+1为front的时候,我们便判断这个时候数组已经满了,这个问题便可以解决了。

优化后的链状图
下面有Java代码来实现:

package com.it.data;

public class QueueByArray<T> {
  //内部维护的数组
    private T[] arr;
    //控制删除元素的指针
    private int front;
    //控制插入元素的指针
    private int rear;
    public QueueByArray(){
        this(15);
    }

    public QueueByArray(int capatity){
        arr = (T[])new Object[capatity+1];
    }

    //进队列
    public int offer(T t){
        if(rear+1 == arr.length ) {
            if (front == 0) {
                throw new IllegalArgumentException("队列已满,无法插入");
            } else {
                arr[rear] = t;
                rear = 0;
            }
        }else{
            arr[rear] = t;
            rear++;
        }
        return rear;
    }

    //出队列
    public T poll(){
        if (rear == front){
            throw new IllegalArgumentException("队列中暂无数据");
        }
        T temp = arr[front];
        arr[front] = null;
        if(front == arr.length -1 ){
            front=0;
        }else {
            front++;
        }
        return  temp;
    }

    //查询最新元素
    public T peek(){
        if(rear==front){
            throw new IllegalArgumentException("队列中暂无数据");
        }else if(rear == 0 ){
            return arr[arr.length];
        }else{
            return arr[rear-1];
        }
    }

    public static void main(String args[]){
        QueueByArray qba = new QueueByArray(3);
        for (int i = 0; i < 3; i++) {
            qba.offer(1);
            System.out.println(i);
        }
        for(int i = 0;i<4 ; i++){
            qba.poll();
        }
    }
}

再看下输出结果:

0
1
2
Exception in thread "main" java.lang.IllegalArgumentException: 队列中暂无数据
	at com.it.data.QueueByArray.poll(QueueByArray.java:37)
	at com.it.data.QueueByArray.main(QueueByArray.java:67)

当然,用这种抛出异常的方法处理其实是很不合适的,这时候我们可以抛出一个null,然后指针不变也可以。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值