duan2

导航

 

 1.队列的介绍

  队列(queue )简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许
在表的一端进行插入,而在表的另一端进行删除。在队列中把插入数据元素的一端称为 队尾
rear,删除数据元素的一端称为 队首(front) )。向队尾插入元素称为 进队或入队,新元素
入队后成为新的队尾元素;从队列中删除元素称为 离队或出队,元素出队后,其后续元素成
为新的队首元素。
  由于队列的插入和删除操作分别在队尾和队首进行,每个元素必然按照进入的次序离
队,也就是说先进队的元素必然先离队,所以称队列为 先进先出表(First In First Out,简称
FIFO)。队列结构与日常生活中排队等候服务的模型是一致的,最早进入队列的人,最早得
到服务并从队首离开;最后到来的人只能排在队列的最后,最后得到服务并最后离开。

1.1队列的抽象定义

public interface Queue {
    /**
     *返回队列大小
     */
    public int getSize();

    /**
     * 判断队列是否为空
     */
    public  boolean isEmpty();

    /**
     *数据元素e进入队列
     */
    public void enqueue(Object e);

    /**
     * 队首出队元素
     */
    public Object dequeue() throws QueueEmptyException;

    /**
     *取队首元素
     */
    public Object peek() throws QueueEmptyException;
}

1.2队列的顺序存储实现

  在队列的顺序存储实现中,我们可以将队列当作一般的表用数组加以实现,但这样做的
效果并不好。尽管我们可以用一个指针 last 来指示队尾,使得 enqueue 运算可在Ο(1)时间内
完成,但是在执行 dequeue 时,为了删除队首元素,必须将数组中其他所有元素都向前移动
一个位置。这样,当队列中有 n 个元素时,执行 dequeue 就需要Ο(n)时间。
为了提高运算的效率,我们用另一种方法来表达数组中各单元的位置关系。设想数组
A[0.. capacity-1]中的单元不是排成一行,而是围成一个圆环,即 A[0]接在 A[capacity-1]的后
面。这种意义下的数组称为循环数组,如图 4-3 所示。

 

 

  用循环数组实现的队列称为循环队列,我们将循环队列中从队首到队尾的元素按逆时针
方向存放在循环数组中一段连续的单元中。并且直接用队首指针 front 指向队首元素所在的
单元,用队尾指针 rear 指向队尾元素所在单元的后一个单元。如图 4-3 所示,队首元素存储
在数组下标为 0 的位置,front=0;队尾元素存储在数组下标为 2 的位置,rear=3。
当需要将新元素入队时,可在队尾指针指示的单元中存入新元素,并将队尾指针 rear 按
逆时针方向移一位。出队操作也很简单,只要将队首指针 front 依逆时针方向移一位即可。
容易看出,用循环数组来实现队列可以在Ο(1)时间内完成 enqueue 和 dequeue 运算。执行一
系列的入队与出队运算,将使整个队列在循环数组中按逆时针方向移动。
  当然队首和队尾指针也可以有不同的指向,例如也可以用队首指针 front 指向队首元素
所在单元的前一个单元,或者用队尾指针 rear 指向队尾元素所在单元的方法来表示队列在
循环数组中的位置。但是不论使用哪一种方法来指示队首与队尾元素,我们都要解决一个细
节问题,即如何表示满队列和空队列。
  下面以图 4-3 所示的表示方法来说明这个问题。在图 4-3 中用队首指针front指向队首元
素所在的单元,用队尾指针rear指向队尾元素所在单元的后一个单元。如此在图 4-4(b)中
所示循环队列中,队首元素为e 0 ,队尾元素为e 3 。当e 4 、e 5 、e 6 、e 7 相继进入队列后,如图 4-4
(c)所示,队列空间被占满,此时队尾指针追上队首指针,有rear = front。反之,如果从图
4-4(b)所示的状态开始,e 0 、e 1 、e 2 、e 3 相继出队,则得到空队列,如图 4-4(a)所示,此
时队首指针追上队尾指针,所以也有front = rear。可见仅凭front与rear是否相等无法判断队

 

java数组实现:

public class QueueArray implements Queue {

    private static final int CAP = 7; //队列默认大小
    private Object[] elements;         //数据元素数组
    private int capacity;               //数组的大小,elements.length
    private int front;                  //队首指针,指向队首
    private int rear;                   //队尾元素,只想队尾后一个位置

    public QueueArray() {
        this(CAP);
    }

    public QueueArray(int cap) {
        capacity = cap + 1;
        elements = new Object[capacity];
        front = rear = 0;
    }

    @Override
    public int getSize() {
        return (rear - front + capacity) % capacity;
    }

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

    @Override
    public void enqueue(Object e) {
        if (getSize() == capacity - 1) {
            expandSpace();
        } else {
            elements[rear] = e;
            rear = (rear + 1) % capacity;
        }
    }

    private void expandSpace() {
        Object[] a = new Object[elements.length * 2];
        int i = front;
        int j = 0;
        while (i != rear) {
            a[j++] = elements[i];
            i = (i + 1) % capacity;
        }
        elements = a;
        capacity = elements.length;
        front = 0;
        rear = j; //设置新的队首、队尾指针
    }

    @Override
    public Object dequeue() throws QueueEmptyException {
        if (isEmpty()) {
            throw new QueueEmptyException("错误:队列为空");
        }
        Object obj = elements[front];
        elements[front] = null;
        front = (front + 1) % capacity;
        return obj;
    }

    @Override
    public Object peek() throws QueueEmptyException {
        if (isEmpty()) {
            throw new QueueEmptyException("错误:队列为空");
        }
        Object obj = elements[front];
        return obj;
    }
}

 

1.3队列的链式存储实现

  队列的链式存储可以使用单链表来实现。为了操作实现方便,这里采用带头结点的单链
表结构。根据单链表的特点,选择链表的头部作为队首,链表的尾部作为队尾。除了链表头
结点需要通过一个引用来指向之外,还需要一个对链表尾结点的引用,以方便队列的入队操
作的实现。为此一共设置两个指针,一个队首指针和一个队尾指针,如图 4-5 所示。队首指
针指向队首元素的前一个结点,即始终指向链表空的头结点,队尾指针指向队列当前队尾元
素所在的结点。当队列为空时,队首指针与队尾指针均指向空的头结点。

 

 

 java链表实现:

public class QueueSLinked implements Queue {
    private SLNode front;
    private SLNode rear;
    private int size;

    public QueueSLinked() {
        this.front = new SLNode();
        this.rear = front;
        this.size = 0;
    }

    @Override
    public int getSize() {
        return size;
    }

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

    @Override
    public void enqueue(Object e) {
        SLNode p=new SLNode(e,null);
        rear.setNext(p);
        rear=p;
        size++;
    }

    @Override
    public Object dequeue() throws QueueEmptyException {
        if (size<1)
            throw  new QueueEmptyException("错误:队列为空");
        SLNode p = front.getNext();
        front.setNext(p.getNext());
        size--;
        if (size<1)
            rear=front;//如果队列为空,
        return p.getData();
    }

    @Override
    public Object peek() throws QueueEmptyException {
        if (size<1)
            throw  new QueueEmptyException("错误:队列为空");
        return front.getNext().getData();
    }
}

 

posted on 2020-04-30 21:24  duan2  阅读(330)  评论(0编辑  收藏  举报