自己动手实现java数据结构(四)双端队列

 

1.双端队列介绍

  在介绍双端队列之前,我们需要先介绍队列的概念。和栈相对应,在许多算法设计中,需要一种"先进先出(First Input First Output)"的数据结构,因而一种被称为"队列(Queue)"的数据结构被抽象了出来(因为现实中的队列,就是先进先出的)。

  队列是一种线性表,将线性表的一端作为队列的头部,而另一端作为队列的尾部。队列元素从尾部入队,从头部出队(尾进头出,先进先出)。

  双端队列(Double end Queue)是一种特殊的队列结构,和普通队列不同的是,双端队列的线性表两端都可以进行出队和入队操作。当只允许使用一端进行出队、入队操作时,双端队列等价于一个栈;当限制一端只能出队,另一端只能入队时,双端队列等价于一个普通队列。

  简洁起见,下述内容的"队列"默认代表的就是"双端队列"。

2.双端队列ADT接口

复制代码
/**  * 双端队列 ADT接口  * */public interface Deque<E>{      /**      * 头部元素插入      * */    void addHead(E e);      /**      * 尾部元素插入      * */    void addTail(E e);      /**      * 头部元素删除      * */     E removeHead();      /**      * 尾部元素删除      * */     E removeTail();      /**      * 窥视头部元素(不删除)      * */     E peekHead();      /**      * 窥视尾部元素(不删除)      * */     E peekTail();      /**      * @return 返回当前队列中元素的个数      */    int size();      /**      * 判断当前队列是否为空      * @return 如果当前队列中元素个数为0,返回true;否则,返回false      */    boolean isEmpty();      /**      * 清除队列中所有元素      * */    void clear();      /**      * 获得迭代器      * */     Iterator<E> iterator(); }
复制代码

3.双端队列实现细节

3.1 双端队列基于数组的实现(ArrayDeque)

  双端队列作为一个线性表,一开始也许会考虑能否像栈一样,使用向量作为双端队列的底层实现。

  但是仔细思考就会发现:在向量中,头部元素的插入、删除会导致内部元素的整体批量的移动,效率很差。而队列具有"先进先出"的特性,对于频繁入队,出队的队列容器来说,O(n)时间复杂度的单位操作效率是无法容忍的。因此我们必须更进一步,从更为基础的数组结构出发,实现我们的双端队列。

3.1.1 数组双端队列实现思路:

  在进行代码细节的展开之前,让我们先来理解以下基本思路:

  1.和向量一样,双端队列在内部数组容量不足时,能和向量一样动态的扩容。

  2.双端队列内部维护着"头部下标"、"尾部下标"。头部下标指向的是队列中第一位元素尾部下标指向的是下一个尾部元素插入的位置

     从头部下标起始,到尾部下标截止(左闭右开区间),连续保存着队列中的全部元素。在元素出队,入队时,通过移动头尾下标,进行队列中元素的插入、删除,从而避免了类似向量中大量内部元素的整体移动。

     当头部元素入队时,头部下标向左移动一位头部元素出队时,头部下标向右移动一位。

     当尾部元素入队时,尾部下标向右移动一位尾部元素出队时,尾部下标向左移动一位。

  3.当元素下标的移动达到了边界时,需要将数组从逻辑上看成一个环,其头尾是相邻的:

    下标从数组第0位时,向左移动一位,会跳转到数组的最后一位。

    下标从数组最后一位时,向右移动一位,会跳转到数组的第0位。

   下标越界时的跳转操作,在细节上是通过下标取模实现的。

   

3.1.2 队列的基本属性:

  只有当队列为空时,头部节点和尾部节点的下标才会相等。

复制代码
/**  * 基于数组的 双端队列  * */
                        
关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信