队列之-循环队列

一、普通队列的弊端

队列:是一种可以分别在两端进行增删的特殊线性表。既然是线性表,那么可以使用顺序存储和链式存储来实现,如果是链式存储的话,那么增删的复杂度都是O(1),这一点很好理解,应该只要修改一下指针就好了,如果是顺序存储的话,在队尾增加的时间复杂度是O(1),但是在队头进行删除的时候,会涉及到迁移操作,这时候的时候复杂度就是O(n),所以一般来讲不会直接使用顺序存储的方式来实现普通队列。


image.png

二、循环队列的原理概述

较之普通队列用顺序存储来实现存在删除带来的时间损耗,怎么去避免它,因为普通队列删除队头结点,会将后续结点进行整体的一个前移,所以才会带来O(n)的时间复杂度,如果不前移结点,而是调整头结点的位置,删除一个结点,就将头结点往后移动一位。

TIP:front是指向头结点的指针,rear是指向下一个要插入结点的指针


队列初始状态.png
队列删除结点后的状态.png

那么这样子是可以避免删除带来的时间损耗了,但是可以发现当往下标4插入数据后,rear指针该何去何从,放在脚标5?那么会报数组越界,而且很明显下标0和1的位置都空着,所以rear应该指向0才对,那么这种实现方式其实就是循环列表。

循环队列.png

实现循环列表存在几个要考虑的事情:
1:啥时候表示队列空着?
一般在循环队列的初始状态,可以将front和rear指针都指向下标0,当插入元素时,rear会顺时针方向累加,当删除元素时,front也会顺时针方向累加,那么删的和加的一样多,其实在同一个方向走的下标也一样多,那么自然容易想到当front == rear时,表示队列为空。只不过这里需要注意一点,因为是循环队列,front和rear的值不能无限递增,比如一个数组,大小为5,最大下标是4,难道还能4+1=5,出来个5下标,明显不可能,所以,这里的递增,需要这么操作:
(front + 1) % maxSize,巧妙的用到了取模运算符。

2:啥时候表示队列满了?
队列啥时候算满呢?其实可以发现front可能在rear的后面,也有可能在rear的前面,仔细想想可能会发现某种情况下,当rear == front也是队列满的时候,但是这样就跟队列空着的时候,判断条件一致了,这样明显也不行,为了区分开来,单独留一个结点不存值,当rear和front相差一个结点时,即证明循环队列已满。判断条件为:(rear + 1) % maxSize = front


循环队列满的状态.png

3:怎么才能知道队列当前含有元素的多少?
由于循环队列的特性,队列里面的元素可能是连续的,也有可能是分成两段的,那么怎么去统计含有多少元素呢?可以使用这个公式(rear - front + maxSize) % maxSize,因为rear - front可能为正数,也有可能会负数,为正数表示是连续的,为负数表示不连续的。

三、循环队列的实现-java

public class CircularQueue<E> {
    /**
     * 循环队列
     */
    /**
     * 循环队列常用的方法如下:
     * 1、InitCircularQueue()    初始化一个循环队列
     * 2、ClearCircularQueue()   清空一个循环队列
     * 3、CircularEmpty()    判断循环队列是否为空
     * 4、GetHead()  获取循环队列尾部结点数据
     * 5、EnCircularQueue()  在循环队列尾部插入新结点
     * 6、DeCircularQueue()  删除循环队列头部结点
     * 7、CircularQueueLength()  返回循环队列的长度
     */

    Object[] queueArray = null;
    int front; //队头指针
    int rear; //队尾指针
    int maxQueueSize;
    public CircularQueue(int maxSize){
        // 初始化循环队列的基础存储结构,数组
        this.maxQueueSize = maxSize;
        this.queueArray = new Object[maxSize];
        this.front = 0; //队头指针指向循环队列的第一个结点,初始为0
        this.rear = 0;//队尾指针指向循环队列的待插入结点位置,初始为0
    }
    public void ClearCircularQueue(){
        if (front == rear){return;}
        //清空一个循环队列,只需要从front开始,一直到rear-1,将节点元素赋值成null
        for (;front != rear;){
            DeCircularQueue();
        }
    }

    public Boolean CircularEmpty(){
        if (front == rear){return true;}
        else{return false;}
    }

    public E GetHead(){
        return (E)queueArray[front];
    }

    public void EnCircularQueue(E elem){
        //满足(rear+1)%maxSize=front说明该循环队列已经达到满队列状态,无法在插入
        if ((rear + 1) % maxQueueSize == front){
            return;
        }
        queueArray[rear] = elem;
        //循环队列在rear指针时,如果只是简单累加,很可能会出现空指针异常
        rear = (rear + 1) % maxQueueSize;
    }

    public E DeCircularQueue (){
        //满足front = rear时,说明队列为空
        if (front == rear){return null;}
        E returnElem = (E)queueArray[front];
        queueArray[front] = null;
        front = (front + 1) % maxQueueSize;
        return returnElem;
    }

    public int CircularQueueLength(){
        return (rear - front + maxQueueSize) % maxQueueSize;
    }

    public String toString(){
        StringBuffer stringBuffer = new StringBuffer();
        for (int index = 0;index < maxQueueSize;index++){
            if (index + 1 < maxQueueSize){
                stringBuffer.append(queueArray[index]);
                stringBuffer.append(",");
            }else{
                stringBuffer.append(queueArray[index]);
            }
        }
        return stringBuffer.toString();
    }

    public static void main(String[] args) {
    }
}

四、循环队列时间复杂度分析

可以很明显发现,循环队列的增删操作时间复杂度都是O(1),并且还能实现充分利用申请到的空间,所以如果能够确认一个队列的大小,那么就使用顺序方式来实现,并且是循环队列,而不是普通队列。如果确认不了,那么还是使用链式方式来实现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容