• 队列


     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();
        }
    }
  • 相关阅读:
    ElasticSearch6学习(1)-安装Elasticsearch
    Ubuntu 18.04 安装java8
    windows10 php7安装mongodb 扩展
    https加密解密过程详解
    Beanstalkd,zeromq,rabbitmq的区别
    PHP中的++和--
    win10 git bash 闪退
    谈下WebSocket介绍,与Socket的区别
    Bridge桥接模式(结构型模式)
    Apater适配器模式(结构型模式)
  • 原文地址:https://www.cnblogs.com/duan2/p/12811232.html
Copyright © 2020-2023  润新知