当前位置:首页 » 《关于电脑》 » 正文

手撕数据结构 —— 队列(C语言讲解)

12 人参与  2024年10月15日 19:21  分类 : 《关于电脑》  评论

点击全文阅读


目录

1.什么是队列

2.如何实现队列 

3.队列的实现

Queue.h中接口总览

具体实现

结构的定义

初始化

销毁

入队列

出队列

取队头元素

取队尾元素

判断是否为空

获取队列的大小 

4.完整代码附录

Queue.h

Queue.c


1.什么是队列

队列是一种特殊的线性表,只允许在一端进行数据的插入操作,在另一端进行数据的删除操作。队列中的数据元素必须满足先进先出的性质。

插入数据的操作叫做入队列,插入数据的一端叫做队尾。删除数据的操作叫做出队列,删除数据的一端叫做队头

队列逻辑结构如下图所示:

2.如何实现队列 

要想实现队列,必须满足队列的要求,也就是以下两点:

a、在一端插入数据,在另一端删除数据。b、满足先进先出的性质。

所以,我们使用数组(顺序表)或者 链表实现,那么使用哪种结构来实现更好呢?

我们可以对比一下:

数组(顺序表)链表(单链表)
头插效率O(N)O(1)
头删效率O(N)O(1)
尾插效率O(1)O(N)
尾删效率O(1)O(N)

貌似使用数组和链表实现都是一样的,在一端进行插入or删除的效率是O(1),在另一端进行删除or插入的效率就是O(N) 。

但是我们可以优化一下链表,链表在尾部进行插入or删除的效率之所以是O(N),是因为需要遍历链表找尾结点,但是,如果我们能够直接找到尾结点,那么链表在尾部进行插入和删除的效率不就都是O(1)了吗?

所以,我们可以使用两个指针head和tail分别指向链表的头结点和尾结点。这样一来,进行尾插时,直接就能找到尾结点,所以尾插的效率也是O(1)。

所以我们实现的队列的最终形态如下:

3.队列的实现

队列的实现,我们主要实现Queue.h和Queue.c文件即可,Queue.h文件中存放声明,Queue.c文件中存放定义。

Queue.h中接口总览

具体实现

结构的定义

实现队列结构的时候,由于我们采用的是链表来实现队列,所以我们需要定义一个个的结点;前面我们分析可知,head指向头结点,tail指向尾结点,可以使得入队列和出队列的时间复杂度都为O(1),所以我们需要管理好head指针和tail指针,这里我们也采用结构体来管理。

初始化

初始化队列只需要初始化 struct Queue 结构体对象中的成员即可。

head和tail指针都初始化为空。size初始化为0。

 

销毁

销毁队列的时候,首先需要将节点全部释放,然后将head和tail指针置空,并将size置为0。

入队列

入队列的时候要区分两种情况:

入队列的结点是第一个结点,此时让head和tail同时指向该结点即可。入队列的结点不是第一个结点,此时让新结点连接到尾结点的后面即可。最后记得将size++。

出队列

出队列的时候要区分三种情况:

队列不能为空。只有一个结点的情况。此时,释放结点之后,将head和tail指针置空。不止一个结点的情况。此时,记录head指向的结点,将head后移,然后释放记录的节点。

注意:最后记得将size--。

  

取队头元素

直接返回head指向的结点的数据即可。

取队尾元素

直接返回tail指向的结点的数据即可。

判断是否为空

当队列为空时,head和tail都指向空,所以直接判断head或者tail是否为空都可以。

获取队列的大小 

直接返回struct Queue结构体类型对象中的size即可。

4.完整代码附录

Queue.h

#include<stdio.h>#include<stdlib.h>#include<assert.h>#include<stdbool.h>typedef int QDataType;typedef struct QueueNode    // 定义结点 {struct QueueNode* next; // 存储下一个结点的地址 QDataType data;         // 存储数据 }QNode;typedef struct Queue        // 定义队列 {QNode* head;            // 指向头结点 QNode* tail;            // 指向尾结点 int size;               // 记录队列的大小 }Que;// 初始化 void QueueInit(Que* pq);// 销毁 void QueueDestroy(Que* pq);// 入队列 void QueuePush(Que* pq, QDataType x);// 出队列 void QueuePop(Que* pq);// 取队头元素 QDataType QueueFront(Que* pq);// 取队尾元素 QDataType QueueBack(Que* pq);// 判断是否为空 bool QueueEmpty(Que* pq);// 获取队列的大小 int QueueSize(Que* pq);

Queue.c

// 初始化队列 void QueueInit(Que* pq){assert(pq);pq->head = pq->tail = NULL;pq->size = 0;}// 销毁队列 void QueueDestroy(Que* pq){assert(pq);QNode* cur = pq->head;while (cur)             // 遍历释放结点 {QNode* next = cur->next;free(cur);cur = next;}pq->head = pq->tail = NULL;pq->size = 0;}// 入队列 void QueuePush(Que* pq, QDataType x){assert(pq); // pq指针不能为空 QNode* newnode = (QNode*)malloc(sizeof(QNode)); // 申请新结点 if (newnode == NULL){perror("malloc fail");exit(-1);}// 初始化新结点中的成员 newnode->data = x;newnode->next = NULL;if (pq->tail == NULL)  // 入队列的结点是第一个结点 {pq->head = pq->tail = newnode;}else                   // 入队列的结点不是第一个结点 {pq->tail->next = newnode;pq->tail = newnode;}pq->size++;}// 出队列void QueuePop(Que* pq){assert(pq);assert(!QueueEmpty(pq));      // 出队列的时候,队列不能为空 if (pq->head->next == NULL)   // 只有一个结点的情况 {free(pq->head);pq->head = pq->tail = NULL;}else                          // 不止一个结点的情况 {QNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;}// 取队头元素 QDataType QueueFront(Que* pq){assert(pq);assert(!QueueEmpty(pq));return pq->head->data;}// 取队尾元素 QDataType QueueBack(Que* pq){assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;}// 判空 bool QueueEmpty(Que* pq){assert(pq);return pq->head == NULL;}// 获取队列的大小 int QueueSize(Que* pq){assert(pq);return pq->size;}

 


点击全文阅读


本文链接:http://zhangshiyu.com/post/172326.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1