一. 什么是队列?

首先我们来了解一下什么是队列,顾名思义,就是队列,哈哈哈,例如军训时的队列,排队时的队列,而军训的队列,你可以从你的左右两边的空间出列,排队时你不想排了,你也可以从左右两边的空间出列。

但数据结构中的“队列”是一维的,只有前后,而如果是在一个队列中的元素,要想出去,只有等前面的元素出队之后才能出队。

二. 那么如何实现队列呢?

首先要知道队列是线性的,一列的,不可能从其中还分出一支路出来吧,要分出来的话,那就叫树了。那么基于这种观点,队列的实现就可以用线性表来实现了,而线性表有两种:顺序表、链表。

三. 顺序表实现队列

  1. 思路: (1)入队:首先设置这个队列所包含的最大容量,第二设置两个指针变量分别指向首尾,第三在队尾插入元素,同时将尾指针加一(注意:这里尾指针可以指向队尾元素,也可以指向队尾的下一位元素,而两种方式会导致插入操作和判空手法有些许区别,具体请看下面的实现)。

    (2)出队:首先找到头指针的位置,第二按顺序表存取元素的方式将此位置的元素取出来,放入变量中返回,之后头指针向前加一。

  2. 实现代码:

// 创建队列的结构体
typedef struct Queue{int data[10]; //用静态数组来存储数据。最大为10,这个是自定义的int frist,last; // 这两个变量用来表示头指针和尾指针int size; // 用来表示当前队列的长度
}Queue;// 创建初始化函数,令首尾指针指向头元素,data中的数据初始化为0,入队时在last所指的位置入队
// 这时,last实际上指的是队尾元素的下一位
bool InitQ(Queue &Q){Q.frist=Q.last=0;  // 若初始化时Q.last= 10-1;此时last实际上指的是队尾的元素,入队时在last的后一位入Q.data[10] = {0};Q.size = 0;return true;
}// 入队操作
bool InputQ(Queue &Q, int x){// 首先判断队列是否满if(Q.size == 10){ // 队列的最大容量为10return false;}else{Q.data[Q.last] = x;Q.last = (Q.last + 1) % 10; //last+1,将last往后走一步,对10取模,让last可以在数组的往复指向// 若初始化时last指向队尾的话,那么入队时要先将上面两个步骤的顺序颠倒Q.size++; //每执行一次入队操作,队列的大小+1return true;}
}// 出队操作
bool OutputQ(Queue &Q, int &x){// 首先判断队列是否空if (Q.size == 0){return false;}else{x = Q.data[Q.frist]; // 将首元素赋给x返回Q.data[Q.frist] = 0; // 出队之后将frist的位置的元素置为默认值Q.frist = (Q.frist +1)%10; // 将first向前移动Q.size--; // 出队成功,队列大小-1return true;}
}int main(){Queue Q;InitQ(Q);printf("队列的第一个元素是:%d\n ",Q.data[Q.frist]); //0InputQ(Q,6);printf("入队一次之后队列的第一个元素是:%d\n ",Q.data[Q.frist]); //6printf("此时队列的大小是:%d\n ",Q.size); //1int x = 0;OutputQ(Q,x);printf("执行一次出队操作后队列的第一个元素是:%d\n",Q.data[Q.frist]); //0printf("出队的元素是:%d\n",x); // 6printf("此时队列的大小是:%d\n",Q.size); // 0return 0;
}
  1. 思考:如果不设置size来表示队列长度的话,那么还可以用什么方式来进行判断队空或者队满呢?

     1.如果不设置变量的话,队空和队满时first和last都可能和原来相同,所以要空上一个存储空间来区分队空和队满时frist和last不同例如队空时 frist == last,队满时frist ==(last+1)%10 2.可以设置一个t变量,当进行入队操作时令t=1,队满的条件则为frist==last && t==1,因为是执行了入队操作后才导致frist和last相同,所以自然是满队了,那么进行出队操作时令t=0,队空的条件则为frist==last && t==0
    

四. 单链表实现队列

  1. 思路:带头结点的链表:首先创建两个指针指向头结点,第二将尾节点(若是首次入队,尾结点就是头结点)的next指向新节点,第三将last指针指向新的尾结点,第四first指针一直指向头结点。

    不带头结点的链表: 首先创建两个空指针,第二当第一个节点入队时,使frist和last指向此节点,第三当节点再次入队修改last使其指向新节点。

       带头结点和不带头结点的队列区别在于:带头结点的first指针始终指向头结点,当进行出队操作时只需要修改next的指向而不带头结点的队列,出队的节点始终是frist的指向节点,如此,出队之后要修改first的指向
    

首先来看带头结点的队列:

// 顺序表实现队列
// 创建存储数据的节点结构体
typedef struct LinkNode{int data;LinkNode *next;
}LinkNode;//创建队列的结构体,相当于创建了两个节点指针,每个指针均指向一个节点
typedef struct LinkQueue{LinkNode *frist,*last;
}LinkQueue;//初始化队列,使首尾指针指向同一个节点,将头结点的next指向空,data初始值置为0
bool InitQ(LinkQueue &Q){Q.frist=Q.last=(LinkNode *)malloc(sizeof(LinkNode));Q.frist->data=0;Q.frist->next = NULL;return true;
}// 入队操作
bool InputQ(LinkQueue &Q, int x){LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));p->data = x; // 修改p节点的数据p->next = NULL;Q.last->next = p; // 修改尾指针的next指向p节点Q.last = p; // 修改尾指针指向p节点return true;
}// 出队操作
bool OutputQ(LinkQueue &Q, int &x){x = Q.frist->next->data; // 将头结点的下一个节点的数据赋值给x返回// 若最后一个元素出队,即frist的next指向了last,将last指向头结点// 将头结点的next指向下一个节点的nextif(Q.frist->next == Q.last){Q.last = Q.frist;}else{Q.frist->next = Q.frist->next->next; // 将头结点的next指向下一个节点的next,即将队头排出队列之外}return true;}int main()
{LinkQueue Q;InitQ(Q);printf("初始化之后队列的首元素为:%d\n ",Q.frist->data);// 这里打印的结果应为0InputQ(Q, 3);printf("进行一次入队操作后队列的队头为:%d\n", Q.frist->next->data); // 3 头结点的下一个节点才是真正存储数据的节点int x = 0;OutputQ(Q,x);printf("出队的元素为:%d\n", x); // 3printf("进行一次出队操作后队列的队头为:%d\n", Q.frist->next->data); // 这一句代码会报错,意味着头结点后没有节点了return 0;
}

以上就是带头结点的单链表实现的队列了,下面看不带头结点的

typedef struct LinkNode{int data;LinkNode *next;
}LinkNode;//创建队列的结构体,相当于创建了两个节点指针,每个指针均指向一个节点
typedef struct LinkQueue{LinkNode *frist,*last;
}LinkQueue;//初始化队列,使首尾指针指向同一个节点,将头结点的next指向空,data初始值置为0
bool InitQ(LinkQueue &Q){Q.frist=Q.last=(LinkNode *)malloc(sizeof(LinkNode));Q.frist->data=0;Q.frist->next = NULL;return true;
}// 入队操作
bool InputQ(LinkQueue &Q, int x){LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));p->data = x; // 修改p节点的数据p->next = NULL;// 不带头结点的队列需要对第一个元素特殊处理,也就是队头if(Q.frist->next == NULL){Q.frist = p;Q.last = p;}else{Q.last->next = p;Q.last = p;}return true;
}// 出队操作
bool OutputQ(LinkQueue &Q, int &x){x = Q.frist->data; // 将头结点的数据赋值给x返回// 如果最后一个元素出队,则将两个指针置为初始化的值,如果不是,则将队头指向下一个节点if(Q.frist == Q.last){Q.frist->data = Q.last->data = 0;Q.frist->next = Q.frist->next = NULL; }else{Q.frist = Q.frist->next; // 将头结点指向下一个节点,即将队头排出队列之外}return true;}
int main()
{LinkQueue Q;InitQ(Q);printf("初始化之后队列的队头为:%d\n ",Q.frist->data);// 这里打印的结果应为0InputQ(Q, 3);printf("进行一次入队操作后队列的队头为:%d\n", Q.frist->data);int x = 0;OutputQ(Q,x);printf("出队的元素为:%d\n", x); // 3printf("进行一次出队操作后队列的队头为:%d\n", Q.frist->data); // 0return 0;
}

以上就是队列的顺序表和单链表的实现方式了,可能有的地方我没有表述清楚,不明白的欢迎私信或者评论区截图询问。

那么,链表中除了单链表之外还有双链表,可能有人会问,可以用双链表实现队列吗?我的回答是:可以,但没必要。双链表比单链表多了一个指向前一个节点的指针域,那么根据队列的规则:队头出队,队尾入队,那这个前驱节点有什么用呢?是不是没有?哈哈哈,所以它存在只会浪费空间。

但是,队列只是一种基本的数据结构,而在实际应用中,我们往往会面临更加复杂的问题,比如还是排队这个问题,若是想对对排队中的VIP用户
进行特殊操作,那单链表和顺序表实现的队列是不是就不可以了?那么这个时候双链表实现的队列是否会有那么一点用处呢?

2020/10/18

【理解数据结构】队列的实现(C语言)相关推荐

  1. 数据结构——队列的C语言代码实现

    系列文章目录 数据结构--顺序表的C语言代码实现 数据结构--八种链表的C语言代码实现 数据结构--栈的C语言代码实现 数据结构--队列的C语言代码实现 数据结构--堆的C语言代码实现 文章目录 系列 ...

  2. python中栈的描述是_数据结构与算法:Python语言描述 栈和队列.ppt

    数据结构与算法:Python语言描述 栈和队列 迷宫问题 迷宫问题的特点: 存在一集可能位置,一些位置相互连通,一步可达 一个位置可能连通若干位置,出现向前探查的多种可能(有分支) 目标是找到一条路径 ...

  3. 数据结构——链式队列解析(C语言版)

    摘自:数据结构学习--链式队列解析(C语言版) 作者:正弦定理 发布时间:2020-11-26 21:07:08 网址:https://blog.csdn.net/chinesekobe/articl ...

  4. 数据结构(八) -- C语言版 -- 栈和队列 - 队列的设计与实现

    我让你知道我有啥 零.读前说明 一.队列的概述 二.队列的操作 三.队列的两种存储结构的模型概述 四.顺序存储结构的队列及实现 4.1.顺序存储结构的传统队列简易实现与测试 4.2.顺序存储结构的队列 ...

  5. 深入理解数据结构和算法

    hi,大家好,我是阿荣,今天分享一些对数据结构和算法精华总结,希望对大家的面试或者工作有一定的帮助: 看完本文可以学到什么 知道哪些数据结构和算法在实际工作中最常用,最重要 理解一些设计上注意事项(经 ...

  6. 深入理解消息队列(场景,对比,原理和设计思想)

    导语 | 消息队列也通常称为消息中间件,提到消息队列,大部分互联网人或多或少都听过该名词.对于后端工程师而言,更是日常开发中必备的一项技能.随着大数据时代的到来,apache旗下的Kafka一度成为消 ...

  7. 看图轻松理解数据结构与算法系列(合并排序)

    前言 推出一个新系列,<看图轻松理解数据结构和算法>,主要使用图片来描述常见的数据结构和算法,轻松阅读并理解掌握.本系列包括各种堆.各种队列.各种列表.各种树.各种图.各种排序等等几十篇的 ...

  8. 数据结构与算法分析:C语言描述(原书第2版 简体中文版!!!) PDF+源代码+习题答案...

    转自:http://www.linuxidc.com/Linux/2014-04/99735.htm 数据结构与算法分析:C语言描述(原书第2版中文版!!!) PDF+源代码+习题答案 数据结构与算法 ...

  9. java循环队列_Java版-数据结构-队列(循环队列)

    前情回顾 在上一篇,笔者给大家介绍了数组队列,并且在文末提出了数组队列实现上的劣势,以及带来的性能问题(因为数组队列,在出队的时候,我们往往要将数组中的元素往前挪动一个位置,这个动作的时间复杂度O(n ...

  10. c语言点菜菜单程序大学一,数据结构实训报告 c语言点餐系统 net

    <数据结构实训报告 c语言点餐系统 net>由会员分享,可在线阅读,更多相关<数据结构实训报告 c语言点餐系统 net(14页珍藏版)>请在人人文库网上搜索. 1.北京联合大学 ...

最新文章

  1. “数学不好,干啥都不行!”骨灰级程序员:其实你们都是瞎努力
  2. C++语言之继承中的特点
  3. [零基础学JAVA]Java SE应用部分-34.Java常用API类库
  4. Migrate Instance 操作详解 - 每天5分钟玩转 OpenStack(40)
  5. 织梦cms生成首页html的php文件,织梦DedeCMS定时自动生成首页HTML的实现方法
  6. pythontype函数使用_Python astype(np.float)函数使用方法解析
  7. eclipse lombok 标红_无法使Lombok项目在Eclipse上运行
  8. Skywalking-02:如何写一个Skywalking trace插件
  9. 数据3分钟丨Oracle Database 21c终于发布而22c可能直接跳过;2021 OceanBase数据库大赛开启。...
  10. python面试题之如何用Python找出你目前在哪个目录?
  11. cbitmap 从内存中加载jpg_Pytorch数据加载的分析
  12. 使用 JS刷新框架子页面
  13. HDU3501——欧拉函数裸题
  14. 基于matlab的2ASK调制解调仿真
  15. 【算法工程师】成为一名优秀的机器学习算法工程师所需知识及资料汇总-附思维导图
  16. 瘟疫模拟——技术预演与方案设计(Python技术预演)
  17. volatile能保证原子性吗?
  18. ERP与CRM、MRP、PLM、APS、MES、WMS、SRM的关系
  19. redmi ac2100 红米ac2100路由器 padavan固件
  20. matlab中ss函数_matlab ss函数 tf函数

热门文章

  1. 老男孩22期python视频_老男孩Ptython全栈架构师视频教程 Python最新整理完整版22期视频教程 超60G课程容量...
  2. 网站提供的下载IE8很慢 由于Microsoft 联机服务暂时不可用,SmartScreen筛选器无法检查此网站。...
  3. 游戏开发 - 开发流程 - 收集
  4. 关于“#define REG_MEM_BASE (*(volatile unsigend long *)(PA_BAES + 0x00000050))”语句的解析
  5. int正数和负数的原码、反码、补码
  6. 电脑硬件及电脑配置知识大全
  7. QT-QMainWindow布局设置
  8. pytorch基本知识—主要轮子简介
  9. Eclipse配置svn(入门)
  10. mysql入库出库触发器_入库出库后库存自动更新的SQL触发器语句是什么?