数据结构与算法-队列

1. 顺序存储

在使用队列时,我们使用两个变量表示队列的头和尾。

以长度为5的顺序队列为例:

  1. 开始队列头Q.front和队列尾Q.rear相等为0,且在数组头部;
  2. C1、C2、C3入队,Q.front不动,Q.rear指向3。
  3. C1、C2出队,Q.rear不动,Q.front指向2。
单向队列

之前使用过两个位置,我们无法访问,造成了资源的浪费。

如果将Q.frontQ.rear范围的数据移动倒表头,可以解决这个问题,但是这样就太麻烦了。

所以提出了循环队列的概念。

循环队列

Q.rear到达队尾时,会来到表头位置,如上图(a)所示。

但是这样又有了一个新问题:

  • 空队列时,Q.frontQ.rear相等,如上图(c)所示。
  • 当队列被占满,Q.frontQ.rear也相等,如上图(b)所示。

这样就没法判断队列是空队列还是满队列了。

这里,我们牺牲一个存储单元,当Q.rearQ.front之间只空一个存储单元时认为队列是满的,就可以解决这个问题。

1.1 结构定义

#define MAXSIZE 10

typedef int Status;
typedef int QElemType;

typedef struct
{
    QElemType data[MAXSIZE];
    int front;
    int rear;
} SqQueue;

1.2 函数实现

// 1.构建一个空队列S
Status InitQueue(SqQueue *Q) {
    Q->front = Q->rear = 0;
    return OK;
}

// 2.将队列置空
Status ClearQueue(SqQueue *Q) {
    Q->front = Q->rear = 0;
    return OK;
}

// 3.判断队列是否为空
Status QueueEmpty(SqQueue Q) {
    if (Q.rear == Q.front)
        return TRUE;
    else
        return FALSE;
}

// 4.返回队列的长度
int QueueLength(SqQueue Q){
    return (Q.rear + MAXSIZE - Q.front) % MAXSIZE;
}

// 5.获取队列头元素
Status QueueFront(SqQueue Q, QElemType *e){
    if (Q.rear == Q.front) // 队列是空的
        return ERROR;
    else
        *e = Q.data[Q.front];
    return OK;
}

// 6.入队
Status Enqueue(SqQueue *Q, QElemType e){
    if ((Q->rear + 1) % MAXSIZE == Q->front) return ERROR;//队列满了
    Q->data[Q->rear] = e;
    Q->rear = (Q->rear + 1) % MAXSIZE;
    return OK;
}

// 7.出队
Status Dequeue(SqQueue *Q, QElemType *e){
    if (Q->rear == Q->front) return ERROR; // 队列是空的
    *e = Q->data[Q->front];
    Q->front = (Q->front + 1) % MAXSIZE;
    return OK;
}

// 8.从队列头开始遍历
Status QueueTraverse(SqQueue Q){
    int i = Q.front;
    while (i != Q.rear) {
        printf("%d ", Q.data[i]);
        i = (i + 1) % MAXSIZE;
    }
    printf("\n");
    return OK;
}

int main(int argc, const char * argv[]) {
    SqQueue Q;
    int e;
    if (InitQueue(&Q) == OK) {
        for (int j = 1 ; j < 11; j++) {
            Enqueue(&Q, j);
        }
    }
    printf("循环队列中元素为: \n");
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Dequeue(&Q, &e);
    printf("出队: %d\n", e);
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Enqueue(&Q, e);
    printf("入队: %d\n", e);
    QueueTraverse(Q);
    printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
    QueueFront(Q, &e);
    printf("队列头元素: %d \n", e);
    printf("队列长度: %d\n", QueueLength(Q));
    ClearQueue(&Q);
    printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
    printf("队列长度: %d\n", QueueLength(Q));
    
    return 0;
}
// 输出
循环队列中元素为: 
1 2 3 4 5 6 7 8 9 
队列长度: 9
出队: 1
2 3 4 5 6 7 8 9 
队列长度: 8
入队: 1
2 3 4 5 6 7 8 9 1 
是否为空队列: 0 (1:空 0:否)
队列头元素: 2 
队列长度: 9
是否已经清空队列 1 (1:空 0:否)
队列长度: 0

2. 链式存储

链式存储不会遇到顺序存储的问题,所以直接使用单向链表就可以实现。

2.1 结构定义

typedef int Status;
typedef int QElemType;

/* 链栈结构 */
typedef struct QueueNode
{
    QElemType data;
    struct QueueNode *next;
} QueueNode, *QueueNodePtr;

typedef struct
{
    QueueNodePtr front;
    QueueNodePtr rear;
    int count;
} LinkQueue;

2.2 函数实现

无头结点
// 1.构建一个空队列S
Status InitQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    Q->front = Q->rear = NULL;
    Q->count = 0;
    return OK;
}

// 2.将队列置空
Status ClearQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    QueueNodePtr p, q;
    p = Q->front;
    while (p) {
        q = p;
        p = p->next;
        free(q);
    }
    Q->front = Q->rear = NULL;
    Q->count = 0;
    return OK;
}

// 3.判断队列是否为空;
Status QueueEmpty(LinkQueue Q) {
    if (!Q.front)
        return TRUE;
    else
        return FALSE;
}

// 4.返回队列的长度
int QueueLength(LinkQueue Q) {
    return Q.count;
}

// 5.获取队列头元素
Status QueueFront(LinkQueue Q, QElemType *e) {
    if (!Q.front) // 队列是空的
        return ERROR;
    else
        *e = Q.front->data;
    return OK;
}

// 6.入队
Status Enqueue(LinkQueue *Q, QElemType e) {
    if (!Q) return ERROR;
    QueueNodePtr node = (QueueNodePtr)malloc(sizeof(QueueNode));
    if (!node) return ERROR; //没有空间了
    node->data = e;
    node->next = NULL;
    if (!Q->front) Q->front = node;
    if (Q->rear) Q->rear->next = node;
    Q->rear = node;
    Q->count++;
    return OK;
}

// 7.出队
Status Dequeue(LinkQueue *Q, QElemType *e) {
    if (!Q || !Q->front) return ERROR; // 队列是空的
    QueueNodePtr node = Q->front;
    Q->front = node->next;
    if (!Q->front) Q->rear = NULL; // 队列空了
    Q->count--;
    *e = node->data;
    free(node);
    return OK;
}

// 8.从队列头开始遍历
Status QueueTraverse(LinkQueue Q) {
    QueueNodePtr node = Q.front;
    while (node) {
        printf("%d ", node->data);
        node = node->next;
    }
    printf("\n");
    return OK;
}

int main(int argc, const char * argv[]) {
    LinkQueue Q;
    int e;
    if (InitQueue(&Q) == OK) {
        for (int j = 1 ; j < 11; j++) {
            Enqueue(&Q, j);
        }
    }
    printf("链式队列中元素为: \n");
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Dequeue(&Q, &e);
    printf("出队: %d\n", e);
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Enqueue(&Q, e);
    printf("入队: %d\n", e);
    QueueTraverse(Q);
    printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
    QueueFront(Q, &e);
    printf("队列头元素: %d \n", e);
    printf("队列长度: %d\n", QueueLength(Q));
    ClearQueue(&Q);
    printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
    printf("队列长度: %d\n", QueueLength(Q));
    return 0;
}

// 输出
链式队列中元素为: 
1 2 3 4 5 6 7 8 9 10 
队列长度: 10
出队: 1
2 3 4 5 6 7 8 9 10 
队列长度: 9
入队: 1
2 3 4 5 6 7 8 9 10 1 
是否为空队列: 0 (1:空 0:否)
队列头元素: 2 
队列长度: 10
是否已经清空队列 1 (1:空 0:否)
队列长度: 0
有头节点
// 1.构建一个空队列S
Status InitQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    Q->front = Q->rear = (QueueNodePtr)malloc(sizeof(QueueNode)); //生成头结点
    if (!Q->front) return ERROR;
    Q->front->next = NULL;
    return OK;
}

// 2.销毁队列(要包括头结点)
Status DestoryQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    while (Q->front) {
        Q->rear = Q->front->next;
        free(Q->front);
        Q->front = Q->rear;
    }
    Q->count = 0;
    return OK;
}

// 3.将队列置空(不释放头结点)
Status ClearQueue(LinkQueue *Q) {
    if (!Q) return ERROR;
    QueueNodePtr p, q;
    Q->rear = Q->front; // 都指向头结点
    p = Q->front->next; // 释放之后的节点
    Q->front->next = NULL;
    while (p) {
        q = p;
        p = p->next;
        free(q);
    }
    Q->count = 0;
    return OK;
}

// 4.判断队列是否为空;
Status QueueEmpty(LinkQueue Q) {
    if (Q.front == Q.rear)
        return TRUE;
    else
        return FALSE;
}

// 5.返回队列的长度
int QueueLength(LinkQueue Q) {
    return Q.count;
}

// 6.获取队列头元素
Status QueueFront(LinkQueue Q, QElemType *e) {
    if (Q.front == Q.rear) { // 队列是空的
        return FALSE;
    }
    *e = Q.front->next->data;
    return TRUE;
}

// 7.入队
Status Enqueue(LinkQueue *Q, QElemType e) {
    if (!Q) return ERROR;
    QueueNodePtr node = (QueueNodePtr)malloc(sizeof(QueueNode));
    if (!node) return ERROR;
    node->data = e;
    node->next = NULL;
    Q->rear->next = node;
    Q->rear = node;
    Q->count++;
    return OK;
}

// 8.出队
Status Dequeue(LinkQueue *Q, QElemType *e) {
    if (!Q || Q->front == Q->rear) return ERROR; // 队列是空的
    QueueNodePtr node = Q->front->next;
    Q->front->next = node->next;
    Q->count--;
    *e = node->data;
    if (Q->rear == node) Q->rear = Q->front; // *删空后将rear指向头结点
    free(node);
    return OK;
}

// 9.从队列头开始遍历
Status QueueTraverse(LinkQueue Q) {
    QueueNodePtr node = Q.front->next;
    while (node) {
        printf("%d ", node->data);
        node = node->next;
    }
    printf("\n");
    return OK;
}

int main(int argc, const char * argv[]) {
    LinkQueue Q;
    int e;
    if (InitQueue(&Q) == OK) {
        for (int j = 1 ; j < 11; j++) {
            Enqueue(&Q, j);
        }
    }
    printf("链式队列中元素为: \n");
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Dequeue(&Q, &e);
    printf("出队: %d\n", e);
    QueueTraverse(Q);
    printf("队列长度: %d\n", QueueLength(Q));
    Enqueue(&Q, e);
    printf("入队: %d\n", e);
    QueueTraverse(Q);
    printf("是否为空队列: %d (1:空 0:否)\n", QueueEmpty(Q));
    QueueFront(Q, &e);
    printf("队列头元素: %d \n", e);
    printf("队列长度: %d\n", QueueLength(Q));
    ClearQueue(&Q);
    printf("是否已经清空队列 %d (1:空 0:否)\n", QueueEmpty(Q));
    printf("队列长度: %d\n", QueueLength(Q));
    return 0;
}
// 输出
链式队列中元素为: 
1 2 3 4 5 6 7 8 9 10 
队列长度: 10
出队: 1
2 3 4 5 6 7 8 9 10 
队列长度: 9
入队: 1
2 3 4 5 6 7 8 9 10 1 
是否为空队列: 0 (1:空 0:否)
队列头元素: 2 
队列长度: 10
是否已经清空队列 1 (1:空 0:否)
队列长度: 0
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容