Java数据结构和算法-栈和队列(2-队列)

队列(queue)和我们生活中的队伍一样,是一种等待服务的模式,就是站到队列中去。它也是一种数据结构,和栈一样,也是作为程序员的一种辅助工具,但是栈是先进后出,而队列是先进先出,这和我们现实世界的排队是一样的,它可以被用在一些复杂的数据结构算法中,比如图,也可以用于模拟真实世界的环境,比如模拟电影院的排队取票等。
在计算机操作系统当中,队列也有其用武之地。打印作业在打印队列中等待着打印操作。同时在键盘敲击内容到计算机时,若计算机要做其他工作,那么将会利用一个存储键入内容的队列来存储键入值,在每敲击一些键盘时,便会有一个值被存入到队列中。存在队列的数据不会丢失,并且是有序的,它会等待等待文字读取程序来读取它。利用队列来保存这些数据的优点就在于,存储的数据始终是有序的。

队列是如何工作的

队列可以使用数组来实现,也可以使用链表来实现,由于链表还未介绍,所以一些内容队列均匀数组实现。
队列的基本操作有两种,插入和移除。在栈当中这两个操作都有标准的名字:push和pop,但是队列没有,插入可以叫做insert、push和enque,移除可以叫做delete、pop和deque,队尾可以叫tail、back和end,而队头也可以叫做head,在本文中,我们使用insert和delete来描述两个操作,队尾用rear,队头用front。
当队列为空时,队尾指向下标为-1的位置,队头front指向下标为0的位置

插入(insert)

当要插入一个新的数据时,会先将队尾的位置先加1,指向下一个位置,然后在该位置上放入新的数据。

移除(delete)

当要在队列中移除并且返回其值时,会先返回队头fron所指向的值,然后再将front值加1上移。
具体操作过程如下图:
这里写图片描述

队空队满

当想空的队列中移除或者访问数据时,就会报”can’t remove, queue is empty“,当想在已满的队列中插入新的数据时,会报”can’t insert, queue is full“。

循环队列

在上述的操作其实存在着一个问题,如果队尾已经移动到队顶,就不能再插入数据,但此时队列不一定是满的,或者队头已经移动到队顶,此时也不能移除数据,但队列也不一定是空的,这样的话由于使用的是数组实现的,不能扩大队列的长度,那么就只能重新创建一个更大一点队列,这回造成对内存空间的浪费。
那么该怎么处理这个问题呢?

环绕式处理

为了避免上述问题,我们可以在队头或者队尾移动到队顶时,将其重新赋值,是其绕回到下标为0的位置,这就是循环队列(有时也成为缓冲环)。
插入更多的数据项。这个时候当队尾的值为队列所能存贮的大小,而要再插入新的数据时,队尾会绕道下标为0的位置,再赋予新值,此时队尾rear处于队头front的下面,颠倒了初始的位置,这可以被称为”折断的队列“,队列中的数据被存放在数组的两个不同的序列中。然后如果再插入新的数据,队尾又会不断变大而向上移动。如下图:
这里写图片描述
删除更多的数据项。当队头值等于队列所能存贮的大小时,队头会绕到下标为0的位置,指定新的队头,下次再删除数据时,将删除此时队头所指的值。这个时候队列当中的数据又恢复到单一序列。

循环队列的Java代码

class Queue {
    private int maxSize; //表示队列所能存放的大小
    private int[] array; 
    private int nItem; //表示当前已经存放的数据个数
    private int front;
    private int rear;

    public Queue(int max) {
        maxSize = max;
        array = new int[max];
        nItem = 0;
        front = 0;
        rear = -1;
    }

    public void insert(int val) {
        rear ++;
        if(rear == maxSize)
            rear = 0;
        array[rear] = val;
        nItem ++;
    }

    public int delete() {
        int temp = array[front ++];
        if(front == maxSize)
            front = 0;
        nItem --;
        return temp;
    }

    public int peak() {
        return array[front];
    }

    public boolean isEmpty() {
        return (nItem == 0)
    }

    public boolean isFull() {
        return (nItem == maxSize)
    }

    public int size() {
        return nItem;
    }
}//end Queue

在上述代码中,isEmpty()、isFull()和size()方法的实现等使用了记录队列中数据项个数的变量nItem。但是这种使用会使得程序产生额外的操作。

没有数据项计数字段的队列实现

nItem的使用在插入和移除操作不多时,并不会有多大的影响,但是当这两种操作较多时,可能就会影响程序的性能。
因此在某些队列的实现时,并不会使用nItem这个变量,而是利用对队头front和队尾rear的一些计算来代替nItem在程序中的作用。但是由于队尾的环绕,可能会出现折断的队列,所以对于是否为空,或者是否已满的判断会变得复杂。
这个问题是可以解决的,在下列代码中实现了这些操作,在这个代码中数组所能存放的个数maxSize要比队列数据项个数s要大一,即maxSize=s+1,那么在这个时候队头和队尾所能指向的最大下标也变为了maxSize-2,当等于maxSize-1时,就说明要环绕到下标为0的位置:

class Queue {
    private int maxSize;
    private int[] array;
    private int front;
    private int rear;

    public Queue(int s) {
        maxSize = s + 1;
        array = new int[maxSize];
        front = 0;
        rear = -1;
    }

    public void insert(int val) {
        rear ++;
        if(rear == maxSize-1)
            rear = 0;
        array[rear] = val;
    }

    public int delete() {
        int temp = array[front --];
        if(front == maxSize-1)
            front = 0;
        return temp;
    }

    public int peak() {
        return array[front];
    }

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

    public boolean isFull() {
        return ((rear + 1) % (maxSize - 1) == front);
    }

    public int size() {
        if(rear > front)
            return (rear - front + 1);
        else
            return (maxSize - front) + (rear + 1);
    }
}//end Queue

队列的效率

队列的插入和移除也与队列的数据个数无关,所以所要消耗的时间也为O(1)

双端队列

双端队列即在队列两端都可以进行插入和移除数据项的操作。这些方法被称为insertLeft和removeLeft,以及insertRight和removeRight。
如果严格禁止调用某一端的插入和移除,那么这个双端队列就和栈的功能相同。如果禁止调用insertLeft和removeRight,那么这就是一个普通队列。
双端队列集齐了队列和栈的功能,是一种多用途的数据结构,不过似乎也不怎么常用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值