C语言数据结构与算法(栈和队列)

1.栈

1.栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFOLast In First Out的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶

压栈和出栈也遵循后进先出原则

1.2栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。

进栈和出栈示意图:

                        

1.2.1结构设定

利用动态数组实现。

                                

1.2.2初始化

由于是使用动态数组实现的,我们需将数组在初始化是开一些空间,并对结构体中的capaci(容量)和top(指向栈顶的数组下标)进行初始化。

代码实现:

                        

1.2.3入栈

由于是用动态数组实现的。

注意:

①入栈前需判断是否需要扩容。

②Top是指向栈顶的,入栈时应将Top++,在进行入栈。

代码实现及其图解:

                                

1.2.4出栈

注意:

①需判断栈是否为空。

②直接将Top--,由于数组是依靠下标访问的。

代码实现及其图解:

                                

                

1.2.5访问栈顶数据

由于我们前面初始化就将Top指向栈顶数据的下标,因此这里我们直接访问数组的TOP下标即可。

注意:要判断栈是否为空。

代码实现:

                        

1.2.6栈的数据个数

Top指向的是栈顶元素,我们统计栈里的数据个数我们只需要将Top+1即可。

代码实现:

                   

1.2.7销毁

我们是用动态数组实现的,使用完后需将开辟在堆上的数据释放掉,否则会出现内存泄漏。

代码实现:

                

2.队列

2.1队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头。

2.2队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

2.2.1结构设定

①定义链表的结构体

②由于队列涉及链表的头删和尾插,故我们需要在封装个结构体用于指向链表的头节点(对头)和尾节点(队尾),比较高效因此不用遍历链表来找头和尾。

代码实现及其示意图:

head用于指向队头

tail用于指向队尾

size用于记录队列中的数据个数。

2.2.2初始化

对指向头和尾的结构体初始化即可,由于我们队列是依靠此结构体来运行的。

代码实现:

                

2.2.3判断队列是否为空

直接访问size即可,看其是否为零或者看其头和尾指针是否都指向NULL。为空返回 true,不为空则返回 false。

代码实现:

                

2.2.4入队列

入列:即链表的尾插(需注意:①是否为空链表②非空链表)

代码实现及其示意图:

2.2.5出队列

注意:

①队列只有一个数据。

②队列有多个数据。

代码实现及其图解:

2.2.6访问队头数据

由于刚开始就定义了个结构体里面的head和tail指针分别指向队头和队尾,因此可直接访问。

注意:要判断队列是否为空。

代码实现:

                

2.2.7访问队尾数据

同访问队头数据一样的原理。

代码实现:

                

2.2.8队列的数据个数

直接访问结构体中的size。

代码实现:

                

2.2.9销毁

将开在堆上的链表节点释放,返回给操作系统。

代码实现:

                

3.用两个队列实现栈

用两个队列的基础上实现完成栈的功能(遵循后进先出)后面所用的函数都是上面队列的实现的函数。

3.1结构体初始化

须在实现的队列的基础上加多个结构体,成员是两个队列。

代码实现及图解:

3.2初始化

对存放两个队列的结构体进行初始化。

代码实现:

                 

3.3入栈

遵循得出两个队列实现栈的原理:入数据往空队列入。

代码实现及解析:

3.4出栈

把不为空的队列的数据,导入到另一个空队列中,直到剩一个元素(即为要出栈的元素)。

代码实现及解析:

3.5访问栈顶元素

由于我们在队列的实现中,添加了访问队尾的数据(即为栈顶元素)的函数。我们只需要判断一下那个是不为空的队列,然后访问其队尾元素,即可得到我们想要的栈顶元素。

代码实现:

        

3.6判断栈是否为空

我们只需要判断那两个队列中是否为空,为空则为空栈。

代码实现:

3.7销毁

需先销毁结构体中的两个队列,最后在销毁存放两个队列的结构体。

代码实现:

        

4.用两个栈实现队列

遵循先进先出原则。沿用上面栈的实现的接口,两个栈来实现队列具有的性质。和上面两个队列实现栈的原理有点不一样。

原理:(通过画图观察得到)

①一个栈只用来入数据

②另一个栈只用来出数据

4.1结构体的设定

根据原理得,一个栈用来入数据,另一个栈用来出数据,结构体要有两个栈。

代码实现及结构体图示:

4.2初始化

首先需要malloc个结构体(存放两个栈的),然后利用实现栈的初始化,对结构体中的两栈进行初始化即可。

代码实现:

4.3入队列

往专门入数据的那个栈存放数据即可。

代码实现:

                

4.4访问队头数据

注意:

①判断出数据的那个栈是否为空,不为空则直接沿用栈的实现访问栈顶元素的函数(即为队头数据),由于专门出数据的栈,栈里的数据是由入数据哪里倒过来的,所以栈顶元素的数据就是队头数据。 

②出数据的栈为空,需要从入数据的栈,将专门负责入数据的栈的全部数据导入出数据的栈中,然后访问出数据的栈的栈顶元素即可。

代码实现及解析:

        

4.5出队列

①若是专门负责出数据的栈不为空,则直接pop其栈顶数据即可。

若是专门负责出数据的栈为空,则需要将负责专门入数据的栈,入数据的栈里的元素全部导入出数据的栈中,最后pop出数据的栈的栈顶元素即可。

代码实现:原理和上面的访问队头数据一样。

4.6队列是否为空

判断那两个栈是否为空。

代码实现:

4.7销毁

先销毁两个栈,最后再销毁存放两个栈的结构体。

代码实现:

        

5.循坏队列

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。遵循先进先出原则可以用数组实现也可以用链表实现,数组实现的话建议开多一个位置,较容易理解和实现;若是用链表实现的话:解决方案一:结构体中加多个变量size用来记录有效数据的个数。解决方案二:开多一个节点。这里我选择使用数组来实现(当数组超过某个值的时候对其 % 上一个数(k+1)即可控制,防止数组的越界访问)。

5.1结构体的设定

数组实现

代码实现:

                

5.2初始化

首先malloc个结构体,然后在对其成员进行初始化,数组需要开辟k+1个整型空间,front和rear都为零。

代码实现:

                

5.3判断循环队列是否为空

当结构体中的front和rear相等时循环队列才为空。

代码实现及图示:

                        

5.4判断循环队列是否已满

代码实现及图解:

5.5向循环队列插入一个数据

①判断循环队列是否已满。

②特殊情况如下。

代码实现及图示:

5.6从循环队列中删除一个数据

注意:要判断循环队列是否为空。

代码实现及图解:

                        

5.7访问队头元素数据

注意:要判断循环队列是否为空。

代码实现及解析:

        

5.8访问队尾元素数据

注意:有特殊情况如下图。

代码实现及图解:

        

5.9销毁

由于我们数组和结构体都是开在堆上的,故需要 free,将内存空间还给操作系统。

代码实现:

欢迎大家的指导与交流,希望对大家有所帮助,觉得不错的别忘记点赞+收藏咯!!!

谢谢大家。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值