大话西游之王道考研数据结构第四讲---队列

                                                  第四讲---队列

一、队列

队列简称队,一种操作受限的线性表。只允许在一端(队尾)插入,一端(队头)删除(出去)。也就是先进先出的线性表。空队列是指不含任何元素的空表。

举个栗子

上次我们讲到唐僧师徒五人春游时候掉进了一口井里,费了老大的劲几个人才出来。此时,唐僧以及半残了。他们几个准备找个村庄,让师傅休息一下。他们就一路上沿着一条小溪走,林尽水源,便得一山,山有小口,仿佛若有光..初极狭,才通人。(他们看到了一个洞洞)。心想洞洞后面一定有人家,于是乎...这帮人又开始钻洞了:

1.初极狭,才通人~不存在在洞里面,后面那个人把前面那个人超了,或者是并排走的情况。

2.八戒要想出去,必须唐僧先出去,然后猴哥出去,八戒才能出去。

(先进先出)

二、循环队列

传统队列的存储方式一般会造成很大的空间浪费,比如我们申请了50个空间,刚开始时候队头是0,队尾也是0,现在我们加入了40个人,队头是0,队尾是39。现在让着40个人全部出去,队头是39,队尾是39。因为加人是从队尾加的,队尾最大是49,所以只能在加10个人了。前面0~39这40个元素空间就全部浪费了。所以传统的队列一般是完全用不了的。

所以我们采用循环队列,使得队伍真的能达到其预先申请的最大人数。循环队列完全可以理解为一个钟表的形式。在最开始时候:

 我们把整个表分成8个小格,理论上,我们最大可以存储8个个体,但是我们一般存7个(后面会讲到为什么)。

front为队头,指向排在第一个的人;rear为队尾,指向排在最后一个人的下一位(这是规定,这样的话后面一些操作会很方便)。

刚开始时候,队列的头front和尾rear都指向第0个格子,表示队里还没有人。

现在唐僧加进来以后,整个循环队列成了这个样子:

 

所有人都加进来以后:

我们可以再加几个人,方便说明队满时候的状态:

假设我们一共加了8个人,这个时候,可以看到rear和front又同时指向了0号位置,这个状态和我们之前队空时候是一摸一样的。那么如何去检测队列是否满了呢?你可以另外开辟一个变量,记录当前队列人数,通过检测这个变量是否和最大人数相等,来检测队列是否满了。但是我们一般不另外开辟,我们会用一个比较骚的操作去解决这个问题(身为一个程序员,骚是第一位)。我们一般让最大人数是MaxSize-1,这里就是7,这样的话:

我们只需要判断(rear+1)%MaxSize == front就好了,而且只有队满时候,返回值才会是true。

我这里就只讲判断是否队满的操作,插入删除时候指针变化可以看书上,讲的很清楚,也比较简单。和传统队列相比,无非就是增加了一个模运算。

三、循环队列的顺序存储结构

1.老规矩,先说下结构体:

#define MaxSize 50 //最大栈的容量(井里最多放50人)
typedef struct{
    int data[MaxSize];
    int front;//用来表示队头的位置
    int rear;//用来表示队尾的位置
}SqQueue;//一直没有说这个,Sq表示的是线性表的顺序表示Sequence

2.InitQueue(&Q):初始化队列

void InitStack(SqQueue &Q){
    Q.front = 0;
    Q.rear = 0;
}

3.isEmpty(Q):判断是否为空

bool isEmpty(SqQueue Q){
    if(Q.rear == Q.front)//队头等于队尾当然是队里面没人了
        return true;
    else
        return false;
}

4.EnQueue(&Q,x):将x加入队列中

bool EnQueue(SqQueue &Q, int x){
    //1.检查
    if((Q.rear+1)%MaxSize == Q.front)//循环队列判断是否队满
        return false;
    //2.赋值
    Q.data[Q.rear] = x;
    //3.修改
    Q.rear = (Q.rear+1)%MaxSize; //循环队列下递增的方式
    //4.返回成功
    return true;
    
}

5.DeQueue(&Q,&x):将队首元素出队并赋值给x

bool DeQueue(SqQueue &Q,int x){
    //1.检查
    if(Q.rear==Q.front){
        return false;
    }
    //2.赋值
    x = Q.data[Q.front];
    //3.修改
    Q.front = (Q.front+1)%MaxSize;
    return true;
}

6.isFull(Q)检查是否队满:

bool isFull(SqQueue Q){
    if((Q.rear+1)%MaxSize==Q.front){
        return true;
    }
    else{
        return false;
    }
}

7.getSum(Q)

int getSum(SqQueue Q){
    //这里面有个骚操作 + MaxSize
    //这样可以保证%前面那一项始终为正
    //因为我们有可能遇到Q.rear = 1,Q.front= 6的情况
    //而 (a+b)%b == a%b
    //这是取模运算里面的一个定理
    return (Q.rear - Q.front + MaxSize)%MaxSize;
    
}

四、链式队列和双端队列

和链式栈一样,链式队列也不是考试的主要内容。所以不需要太多的了解,核心内容是头指针(front)指向队头结点,尾指针(rear)指向队尾结点(没有头结点,除非特殊说明)。

 

我们主要看双端队列。在循环队列中,只能在队头出队,队尾入队。但是双端队列就不一样了,其两端都可以出队。为了更好理解,我们就把双端队列的两端叫做前端和后端。双端队列一般细分为两种(两端都可以进出的队列一般不考,因为太简单了),一种是输出受限(俩插一删,输出受限,意思就是把出队的那一个操作限制了,出队的操作是删除嘛,所以少一个删除),一种是输入受限(俩删一插)。

书上对双端队列里面,前段进入的元素排列在队列中后端进的元素的前面....xxxx..xx...x。反正我是记不住这些东西~双端队列一般考的是选择题,队列元素一般不超过5个,我们完全可以一个一个试,就可以得到答案了。现在直接做一个题把:

                                   

 

五、习题

猜你喜欢

转载自blog.csdn.net/zhangbaodan1/article/details/81257982
今日推荐