· CSDN的uu们,大家好。这里是C语言数据结构的第七讲。
· 目标:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页: @姬如祎
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
线性表有顺序存储和链式存储,栈是线性表,具有这两种存储方式。同样,队列作为一种特殊的线性表,也同样存在这两种存储方式。
顺序存储的队列就是用数组哈!数组下标为0的一端即是队头。入队列就是在队尾插入一个数据,对于数组而言,不需要移动任何元素,因此时间复杂度是O(1)。
但是在出队列时如果徐翔浪费任何空间的话,就需要将全部数据前移。时间复杂度也就是O(N)。
当然你也可以参照数组模拟队列的思路,维护两个指针:front,rear。front指向队头的元素,rear指向队尾元素。出队列的时候仅需要将front加一即可。这样以来就有空间的浪费。如此看来使用顺序结构来实现队列不是很理想。如果已经确定了队列的元素个数,你可以使用循环队列,这样一来数组来实现队列就是非常完美的事了!关于循环队列,会在栈与队列刷题的那一节提及。
队列的链式存储结构,其实就是线性表的单链表,只不过他只能尾进头出而已,我们把它简称为链队列。
入队就是链表的尾插,我们知道如果不做任何处理的话,单链表尾插的时间复杂度是O(N),为了使得时间复杂度是O(1),我们需要维护一个指针,让他指向链表的尾节点,即是队尾。
出队就是链表的头删,这个操作的时间复杂度是O(1)。
队列的链式存储能够在O(1) 的时间复杂度内实现各种操作,也没有浪费空间。所以我们优先考虑用链表实现队列。
因为我们既要维护指向链表头结点的指针,又要维护指向链表尾节点的指针,并且我们还需要维护一个变量size来记录队列的大小,所以我们将这些变量封装在一个结构体里面,便于操作。
当然你也可以不这么做,那么在函数传参的时候你就需要传多个参数。并且涉及指针的改变,形参还必须是二级指针。
如果有不理解的地方请参考:
http://t.csdn.cn/QXmxD
是不是相当滴麻烦。那么我们就将它们封装成一个结构体吧!
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
//方便维护
typedef int QueueDataType;
//实现队列的链表的节点
typedef struct QueueNode
{
struct QueueNode* next;
QueueDataType data;
} QueueNode;
//将需要维护的head,tail,size封装成结构体
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
int size;
} Queue;
//队列的初始化
void QueueInit(Queue* pq);
//队列的销毁
void QueueDestory(Queue* pq);
//队列是否为空
bool QueueEmpty(Queue* pq);
//队尾增加元素
void QueuePush(Queue* pq, QueueDataType x);
//出队列
void QueuePop(Queue* pq);
//队列的元素个数
int QueueSize(Queue* pq);
//队头元素
QueueDataType QueueFront(Queue* pq);
//队尾元素
QueueDataType QueueBack(Queue* pq);
队列的初始化需要我们做什么呢?将我们封装的结构体里面的变量赋初值!注意对pq的断言,防止发生空指针的解引用。
//队列的初始化
void QueueInit(Queue* pq)
{
//断言
assert(pq);
//赋初值
pq->head = pq->tail = NULL;
pq->size = 0;
}
销毁队列的主要目的:释放堆区开辟的空间,也就是释放单链表的每一个节点。这当然需要遍历呀!我们维护一个指针,例如:cur,用其遍历单链表,让后将遍历到的节点释放。注意保存释放节点的下一个节点就行。还别忘了将我们维护的三个变量:head,tail,size处理一下,防止出现野指针。
//队列的销毁
void QueueDestory(Queue* pq)
{
//断言,防止空指针的解引用
assert(pq);
//cur指针遍历链表
QueueNode* cur = pq->head;
while (cur)
{
//保存cur指针的下一个节点
QueueNode* next = cur->next;
//释放cur指向的节点
free(cur);
//迭代
cur = next;
}
//处理
pq->head = pq->tail = NULL;
pq->size = 0;
}
这个函数的实现有两种方式:
1:就是判断一下头指针是否为空指针就行,头指针为空就说明了链表没有节点,也就说明了队列为空。队列为空时,尾节点此时也是空指针的哦!
2:我们不是维护了一个变量size嘛,其记录了队列的大小,我们只需要判断size是不是0就ok啦!
//队列是否为空
bool QueueEmpty(Queue* pq)
{
//断言,防止空指针的解引用
assert(pq);
//one way
//return pq->size == 0;
//another way
return pq->head == NULL;
}
前面也提到了,入队列的操作就是在链表的尾部插入节点。我们传入的是结构体的指针,想要改变结构体里面的head指针,tail指针是可以做到的,因此队列入数据时注意改变head指针和tail指针就行。
当队列为空时需要特殊处理一下:同时改变head和tail指向开辟的新节点。
//队尾增加元素
void QueuePush(Queue* pq, QueueDataType x)
{
//断言,防止空指针的解引用
assert(pq);
//开辟新的节点
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
if (newNode == NULL)
{
perror("QueuePush::malloc");
exit(-1);
}
else
{
//数据域赋值
newNode->data = x;
newNode->next = NULL;
//当队列为空时
if (!pq->head)
{
pq->head = pq->tail = newNode;
}
else
{
//链表不为空时
pq->tail->next = newNode;
pq->tail = newNode;
}
pq->size++;
}
}
出队列操作,就是链表的头删。理论上我们只需要释放头结点,改变head指针的指向就行。但是,当队列剩下最后一个元素时如果还是仅改变head指针的指向,那么tail指针就会是一个野指针,这样做显然是不正确的。因此我们需要对队列只有一个元素的情况进行特殊判断。
//出队列
void QueuePop(Queue* pq)
{
//防止空指针的解引用
assert(pq);
//队列为空不允许出队列
assert(!QueueEmpty(pq));
//one way
//对一个节点的情况处理
if (pq->head == pq->tail)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
//队列中的元素大于一个
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
//更新size
pq->size--;
//another way
// 都是处理队列中只有一个元素的情况,只不过实现的方式不同
//QueueNode* next = pq->head->next;
//free(pq->head);
//pq->head = next;
//pq->size--;
//if (pq->head == NULL)
// pq->tail = NULL;
}
我们只需要返回我们维护的size变量的值就行。
//队列的元素个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
我们维护了一个指针head,它指向的就是队头元素,返回该节点的打他即可。
//队头元素
QueueDataType QueueFront(Queue* pq)
{
assert(pq);
//队列为空不允许查看队头数据
assert(!QueueEmpty(pq));
return pq->head->data;
}
我们维护了一个指针tail,它指向的就是队尾元素,我们只需要返回该节点的data即可。
//队尾元素
QueueDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------