数据结构学习第二弹 栈与队列(2)

队列

队列,可以说是日常生活中最常见的一种现象,队列与平时排队有着相似的特点。队列也是一种运算受限制的线性表,与栈不同的是,其是限制在两端操作的线性表

定义:

队列就跟日常排队有一样的特点:先进先出,只允许在队列的一端插入数据元素(入队),只允许在队列的另一端删除数据元素(出队),可删除的一端称为队头,可插入的一端称为队尾。
队列的修改总是按照先进先出的原则进行的,也就是说新的元素,只能添加在对尾,每次离开的元素只能从队头离开,就和排队打饭一样,不过这个队不允许中途离队,比如队列中加入元素a1,a2,a3……an,a1就是队头元素,an就是队尾元素,退出队列的顺序也只能是a1,a2……an.

队列的基本操作

  • InitQueue(Q): 构造一个空队列
  • EmptyQueue(Q): 判断一个队列是否为空
  • LengthQueue(Q): 求队列的长度
  • FrontQueue(Q,e): 获取队头元素
  • EnQueue(Q,e): 入队操作
  • DeQueue(Q,e): 出队操作

和栈同理,队列也可以分为顺序队列和链队列。

顺序队

顺序存储结构的类型定义

#define MAXSIZE 20          //单纯的空间大小,并不能代表队列中的元素有多少
typedef int ElemType;
typedef struct
{
    ElemType data[MAXSIZE];
    int front,rear;         //尾指针和头指针
}SeQueue;

具体操作

#define OK 1
#define ERROR 0
typedef int Status;
//将队列初始化置为空
Status InitQueue(SeQueue *q)
{
    q->front = 0;
    q->rear = 0;
    return OK;
}
//判断队列是否为空
int EmptyQueue(SeQueue *q)
{
    if(q->front == q->rear)
        return 1;
    else
        return 0;
}
//获取队列长度
int LengthQueue(SeQueue q)
{
    return q.rear-q.front;          //队尾指针减去队头获得长度
}
//入队操作
Status EnQueue(SeQueue *q,ElemType e)
{
    if(q->rear == MAXSIZE)          //队满
    {
        printf("FULL\n");
        return ERROR;
    }
    q->data[q->rear] = e;           //入队操作
    q->rear++;                      //队尾指针向后移
    return OK;
}
//获取队头元素
Status FrontQueue(SeQueue *q,ElemType *e)
{
    if(EmptyQueue(q))               //队空
    {
        printf("Empty\n");
        return ERROR;
    }
    *e = q->data[q->front];
    return OK;
}
//出队操作
Status DeQueue(SeQueue *q,ElemType *e)
{
    if(EmptyQueue(q))
    {
        printf("Empty\n");
        return ERROR;
    }
    *e = q->data[q->front];         //出队操作
    q->front++;
    return OK;
}

缺点:

由于出队操作和入队操作中,头尾指针只会增加,所以会导致被删除元素无法被重新利用,所以有一种情况,当队列中元素个数少于最大空间时,但也会因为尾指针超过空间上限而不能做入队操作。所以这种队列还是很弱的,但是如果表示成循环队列就能克服这种情况了

循环队列

(脑补长成一条的队列被拗成环的样子)当队尾指针移动到空间的上限位置时,因为这是一个环,所以队尾指针还可以继续移动下去进入到数组最小下标的位置(用取模来实现),这样就可以最大限度的利用存储空间了。
还有一个要解决的问题就是如何判断队满,有两种解决方案:

  • 因为是环所以当头尾指针相等就是队满的情况了,但是一开始的
    时候如果头尾指针相等就是队空了,所以可以多开设一个一个变量
    来记录队空还是队满
  • 还有一个比较简便的方法就是空出一格来,这样队满和队空就可
    以被区分了(下面有这种来处理队满情况)

循环队列的具体操作:

循环队列也是用的顺序存储结构,所以类型定义和顺序队是基本一样的,而在部分运算操作上就有些不同了。

//队列长度
int LengthQueue(SeQueue q)
{
    int len = (q.rear-q.front+MAXSIZE)%MAXSIZE;
    return len;
}
//入队操作
Status EnQueue(SeQueue *q,ElemType e)
{
    if((q->rear+1)%MAXSIZE == q->front)//牺牲多一个空间来判断队满的情况
        return ERROR;
    q->data[rear] = e;              //入队
    q->rear = (q->rear+1)%MAXSIZE;  //尾指针加一并保证循环状态
    return OK;
}
//出队操作
Status DeQueue(SeQueue *q,ElemType *e)
{
    if((q->rear+1)%MAXSIZE == q->front)
        return ERROR;
    *e = q->data[q->front];         //出队
    q->front = (q->front+1)%MAXSIZE;//头指针加一并保证循环意义
}

链队

顾名思义,用链表来表示的队列被称为链队。链队的结点结构和单链表是一样的,只不过队列的插入和删除操作是分别在队尾和队头进行的。因此除了设置头指针外还要设置尾指针在指向队尾,为了方便,这里采用有头结点的单链表来表示队列

队列的链式存储结构类型定义

typedef int ElemType;       //数据类型
typedef struct node
{
    ElemType data;          //数据域
    struct node *next;      //指针域
}QueueNode;     
typedef struct
{
    QueueNode *front;       //头指针
    QueueNode *rear;        //尾指针
}LinkQueue;

链队运算的具体操作

//初始化队列置为空
Status InitQueue(LinkQueue *q)
{
    QueueNode *head;
    head = (QueueNode*)malloc(sizeof(QueueNode));//为头结点开辟存储空间
    if(!head)               //空间申请失败
        return ERROR;
    head->next = NULL;      //头结点指针域置为空
    q->front   = head;      //头指针指向头结点
    q->rear    = head;      //尾指针指向头结点
    return OK
}
//判断队列是否为空
Status EmptyQueue(LinkQueue *q)
{
    return q->rear == q->front;
}
//获取队头元素
Status FrontQueue(LinkQueue *q.ElemType *e)
{
    if(EmptyQueue(q))       //队列为空
        return ERROR;
    *e = q->front->data;
    return OK;
}
//元素入队
Status EnQueue(LinkQueue *q,ElemType e)
{
    QueueNode *s;
    s = (QueueNode*)malloc(sizeof(QueueNode));  //为新结点申请空间
    if(!s)                  //申请空间失败
        return ERROR;
    s->data = e;            //新结点数据域赋值
    s->next = NULL;         //新结点指针域置为空
    q->next->next = s;      //新结点插入到队尾
    q->rear = s;            //更新尾结点
    return OK;
}
//元素出队
Status DeQueue(LinkQueue *q,ElemType *e)
{
    if(EmptyQueue(q))           //队空
        return ERROR;
    QueueNode *s;
    s = q->front->next;         //s指向队头元素
    q->front->next = s->next;   //将s指向的元素移出队列
    if(q->rear = s)             //如果移出的元素是最后一个
        q->rear = q->front;     //队尾指针指向头结点
    *e = s->data;               //用e返回移出的元素值
    free(s);
    return OK;
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,907评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,546评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,705评论 0 238
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,624评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,940评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,371评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,672评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,396评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,069评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,350评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,876评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,243评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,847评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,004评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,755评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,378评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,266评论 2 259

推荐阅读更多精彩内容