1.栈
1.栈的概念及结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last 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,将内存空间还给操作系统。
代码实现:
欢迎大家的指导与交流,希望对大家有所帮助,觉得不错的别忘记点赞+收藏咯!!!
谢谢大家。
标签:队列,代码,栈顶,C语言,实现,数组,数据结构,数据 From: https://blog.csdn.net/2301_81978155/article/details/144868279