当前位置:网站首页>【C语言】详解栈和队列(定义、销毁、数据的操作)
【C语言】详解栈和队列(定义、销毁、数据的操作)
2022-08-05 02:04:00 【要早起的杨同学】
博客主页:要早起的杨同学的博客
欢迎关注点赞收藏️留言
本文所属专栏:【数据结构】
️坚持和努力从早起开始!
参考在线编程网站:牛客网力扣
作者水平有限,如果发现错误,敬请指正!感谢感谢!
文章目录
First. 栈
一. 栈的概念
栈:是一种特殊的线性表,其只允许在表尾进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈 / 压栈 / 入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
二. 进栈出栈的形式
栈对线性表的插入和删除的位置做了限制,并没有对元素进出的时间进行限制,所以,在不是所有元素都进栈的情况下,事先进去的元素也可以出栈,只要保证是栈顶元素出栈就可以。
所以栈是:一种入栈顺序,多种出栈顺序。
比如:现在有元素1、2、3依次进栈,出栈顺序有哪些?
- 第一种:1、2、3(1进、1出、2进、2出、3进、3出)
- 第二种:3、2、1(1、2、3进,3、2、1出)
- 第三种:2、1、3(1、2进,2、1出,3进、3出)
- 第四种:1、3、2(1进、1出,2、3进,3、2出)
- 第五种:2、3、1(1、2进,2出,3进,3出,1出)
三. 栈的存储结构
- 栈的顺序存储结构
数组的首元素作为栈底,另外一端作为栈顶,同时定义一个变量 top 来记录栈顶元素在数组中的位置。
- 栈的链式存储结构
单链表的尾部作为栈底,头部作为栈顶,方便插入和删除(进栈头插,出栈头删),头指针和栈顶指针 top 合二为一。
四. 栈的实现(顺序栈)
- 顺序表的结构相比链表简单,不影响效率的情况下会优先使用顺序表。
- 这次我们用的可动态增长的数组来实现的,因为栈只会在一端进行插入和删除操作,用数组效率还是比较高,当然,也会存在一些问题,就是每次空间不够,要重新开辟空间,可能会造成一些内存浪费。
首先新建一个工程( 博主使用的是 VS2022 )
- Stack.h(栈的类型定义、接口函数声明、引用的头文件)
- Stack.c(栈接口函数的实现)
- Test.c(主函数、测试栈各个接口功能)
Stack.h 头文件代码如下:
#pragma once
#include<stdio.h> /*printf, perror*/
#include<assert.h> /*assert*/
#include<stdlib.h> /*realloc*/
#include<stdbool.h> /*bool*/
typedef int STDataType; //类型重命名,栈中元素类型先假设为int
typedef struct Stack
{
STDataType* a; //指向动态开辟的数组
int top; //记录栈顶位置
int capacity; //栈的容量大小
}Stack;
//初始化栈
void StackInit(Stack* ps);
//销毁栈
void StackDestroy(Stack* ps);
//入栈
void StackPush(Stack* ps, STDataType x);
//出栈
void StackPop(Stack* ps);
//检测栈是否为空,如果为空返回true,否则返回NULL
bool StackEmpty(Stack* ps);
//获取栈中有效元素个数
int StackSize(Stack* ps);
//获取栈顶元素
STDataType StackTop(Stack* ps);
这里重点讲解 Stack.c 中各个接口函数的实现。
4.1 栈的定义
- 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
typedef int STDataType; //类型重命名,栈中元素类型先假设为int
#define N 20
struct Stack
{
STDataType a[N]; //定长数组
int top; //记录栈顶位置
}SqStack;
- 支持动态增长的栈
假设栈的容量 capacity 为 5,定义空栈时 top = -1,当栈存在一个元素时 top = 0
typedef int STDataType; //类型重命名,栈中元素类型先假设为int
typedef struct Stack
{
STDataType* a; //指向动态开辟的数组
int top; //记录栈顶位置
int capacity; //栈的容量大小
}Stack;
4.2 栈的初始化
//初始化栈
void StackInit(Stack* ps)
{
assert(ps);
ps->a = NULL;
ps->top = -1;
ps->capacity = 0;
}
4.3 栈的销毁
//销毁栈
void StackDestroy(Stack* ps)
{
assert(ps);
if (ps->a)
{
free(ps->a);
}
ps->a = NULL;
ps->top = -1;
ps->capacity = 0;
}
4.4 入栈
//入栈
void StackPush(Stack* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity - 1) //检查栈空间是否满了
{
//如果栈原始容量为0,新容量设为4,否则设为原始容量的2倍
int newcapacity = (ps->capacity == 0) ? 4 : (ps->capacity) * 2;
//扩容至新容量
STDataType* newS = realloc(ps->a, newcapacity * sizeof(STDataType));
if (newS == NULL)
{
perror("realloc");
exit(-1);
}
ps->a = newS;
//更新容量
ps->capacity = newcapacity;
}
ps->top++; //栈顶指针加一
ps->a[ps->top] = x; //将新增元素放入栈顶空间
}
4.5 出栈
//出栈
void StackPop(Stack* ps)
{
assert(ps);
assert(ps->top != -1); //栈不能为空
ps->top--; //栈顶指针减一
}
4.6 检测栈是否为空
//检测栈是否为空,如果为空返回true,否则返回NULL
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == -1;
}
4.7 获取栈中有效元素个数
//获取栈中有效元素个数
int StackSize(Stack* ps)
{
assert(ps);
return ps->top + 1;
}
4.8 获取栈顶元素
//获取栈顶元素
STDataType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps)); //栈不能为空
return ps->a[ps->top];
}
五. 测试栈的功能
- 栈的实现,不能像顺序表一样,去实现一个打印函数来遍历栈并输出,这样就不符合栈的特点了(只能在栈顶插入删除,后进先出),所以我们这样来实现出栈:获取并打印栈顶元素,再删除栈顶元素,继续获取新的栈顶元素。
void TestStack() //测试函数
{
Stack s;
//初始化栈
StackInit(&s);
//入栈
StackPush(&s, 1);
StackPush(&s, 2);
StackPush(&s, 3);
StackPush(&s, 4);
StackPush(&s, 5);
//出栈
while (!StackEmpty(&s))
{
printf("%d ", StackTop(&s)); //获取栈顶元素
StackPop(&s);
}
printf("\n");
//销毁栈
StackDestroy(&s);
}
- 运行结果
Second. 队列
一. 队列的概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out) 的特点。入队列:进行插入操作的一端称为队尾 。
出队列:进行删除操作的一端称为队头。
入队出队形式:一种入队顺序,只有一种出队顺序。
队列的应用:比如生活中排队买东西,先排队的先购买,平时我们用微信聊天,用键盘进行各种数字的输入,到聊天框中输出,也是队列的应用。
二. 队列的结构
- 队列的顺序结构
入队,不需要移动任何元素,时间复杂度为O(1)
出队,所有元素需要往前移动,时间复杂度为O(N)
- 队列的链式结构
首先我们定义两个指针,队头指针指向第一个节点,队尾指针指向尾节点
入队(尾插),时间复杂度为O(1)
出队(头删),时间复杂度为O(1)
三. 队列的实现(链式结构)
首先新建一个工程( 博主使用的是 VS2022)
- Queue.h(队列的类型定义、接口函数声明、引用的头文件)
- Queue.c(队列接口函数的实现)
- Test.c(主函数、测试队列各个接口功能)
Queue.h 头文件代码如下:
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int bool;
#define false 0
#define true 1
typedef int QDatatype;
typedef struct QNode
{
struct QNode* next;
QDatatype val;
}QNode;
/* 我们实现无头链表的头插头删接口时,因为需要改变头指针的指向 有以下这样方法: 1、传二级指针 2、改变返回值,返回新的头 3、给链表增加一个哨兵位的头节点 4、结构体包起来 */
typedef struct Queue//队列的链式结构
{
QNode* front;//队头指针
QNode* tail;//队尾指针
}Queue;
//初始化
void queue_init(Queue* qst);
//销毁
void queue_destory(Queue* qst);
//入列
void queue_push(Queue* qst,QDatatype x);
//出列
void queue_pop(Queue* qst);
//队头数据
QDatatype queue_front(Queue* qst);
//队尾数据
QDatatype queue_back(Queue* qst);
//判空
bool queue_empty(Queue* qst);
//大小
int queue_size(Queue* qst);
这里重点讲解 Queue.c 中各个接口函数的实现。
3.1 队列的定义
typedef int QDatatype;
typedef struct QNode//队列节点结构
{
struct QNode* next;//节点指针
QDatatype val;//节点数据
}QNode;
typedef struct Queue//队列的链式结构
{
QNode* front;//队头指针
QNode* tail;//队尾指针
}Queue;
3.2 队列的初始化
//初始化
void queue_init(Queue* qst)
{
assert(qst);
qst->front = qst->tail = NULL;
}
3.3 队列的销毁
//销毁
void queue_destory(Queue* qst)
{
assert(qst);
QNode* cur = qst->front;
while (cur)
{
QNode* Next = cur->next;
free(cur);
cur = Next;
}
qst->front = qst->tail = NULL;
}
3.4 入队(尾插)
要分为两种情况讨论,队列为空和队列不为空
//入列
void queue_push(Queue* qst, QDatatype x)
{
assert(qst);
QNode* newNode = (QNode*)malloc(sizeof(QNode));
newNode->next = NULL;
newNode->val = x;
if (queue_empty(qst))
{
qst->front = qst->tail = newNode;
}
else
{
qst->tail->next = newNode;
qst->tail = newNode;
}
}
3.5 出队(头删)
队列不能为空哦,记得在最开头检查一下
//出队
void queue_pop(Queue* qst)
{
assert(qst);
assert(!queue_empty(qst));
//如果只剩下一个节点,要注意给tail置空
//防止出现野指针
if (qst->front->next == NULL)
{
free(qst->front);
qst->front = qst->tail = NULL;
}
else
{
QNode* del = qst->front;
qst->front = qst->front->next;
free(del);
del = NULL;
}
}
3.6 获取队列元素个数
//获取队列元素个数
//如果会频繁调用这个接口函数,可以在QueuePtr中加一个size记录数据个数
int queue_size(Queue* qst)
{
assert(qst);
QNode* cur = qst->front;
int count = 0;
while (cur)
{
cur = cur->next;
++count;
}
return count;
}
3.7 获取队头元素
队列为空是获取不了元素的哦,记得检查一下
//获取队头元素
QDatatype queue_front(Queue* qst)
{
assert(qst);
assert(!queue_empty(qst));
return qst->front->val;
}
3.8 获取队尾元素
队列为空是获取不了元素的哦,记得检查一下
//获取队尾元素
QDatatype queue_back(Queue* qst)
{
assert(qst);
assert(!queue_empty(qst));
return qst->tail->val;
}
3.9 检查队列是否为空
//检查队列是否为空,若为空返回true,否则返回false
bool queue_empty(Queue* qst)
{
assert(qst);
return qst->front == NULL && qst->tail == NULL;
}
四. 测试队列的功能
void Test(Queue* qst)
{
queue_init(qst);
queue_push(qst, 1);
queue_push(qst, 2);
queue_push(qst, 3);
queue_push(qst, 4);
queue_push(qst, 6);
printf("%d\n", queue_front(qst));
printf("%d\n", queue_back(qst));
printf("%d\n", queue_size(qst));
while (!queue_empty(qst))
{
printf("%d ", queue_front(qst));
queue_pop(qst);
}
queue_destory(qst);
}
- 运行结果
大家快去动手实现一下吧!
边栏推荐
- "Configuration" is a double-edged sword, it will take you to understand various configuration methods
- 2022了你还不会『低代码』?数据科学也能玩转Low-Code啦!
- 如何逐步执行数据风险评估
- Greenplum Database Fault Analysis - Why Does gpstart -a Return Failure After Version Upgrade?
- 力扣-相同的树
- leetcode-另一棵树的子树
- Leetcode刷题——22. 括号生成
- Xunrui cms website cannot be displayed normally after relocation and server change
- source program in assembly language
- [Unity Entry Plan] Handling of Occlusion Problems in 2D Games & Pseudo Perspective
猜你喜欢
Programmer's list of sheep counting when insomnia | Daily anecdote
Understand the recommendation system in one article: Recall 06: Two-tower model - model structure, training method, the recall model is a late fusion feature, and the sorting model is an early fusion
KingbaseES V8 GIS data migration solution (2. Introduction to the capabilities of Kingbase GIS)
迅睿cms网站搬迁换了服务器后网站不能正常显示
Why is this problem reported when installing oracle11
Utilities 第09章 性能分析工具的使用【2.索引及调优篇】【MySQL高级】
Three handshake and four wave in tcp
树形查找(二叉查找树)
如何基于OpenVINO POT工具简单实现对模型的量化压缩
随机推荐
高数_复习_第1章:函数、极限、连续
记录谷歌gn编译时碰到的一个错误“I could not find a “.gn“ file ...”
Live playback including PPT download | Build Online Deep Learning based on Flink & DeepRec
js中try...catch和finally的用法
亚马逊云科技 + 英特尔 + 中科创达为行业客户构建 AIoT 平台
Use of pytorch: Convolutional Neural Network Module
2022了你还不会『低代码』?数据科学也能玩转Low-Code啦!
2022 EdgeX中国挑战赛8月3日即将盛大开幕
MySQL learning
超越YOLO5-Face | YOLO-FaceV2正式开源Trick+学术点拉满
短域名绕过及xss相关知识
AI+小核酸药物|Eleven完成2200万美元种子轮融资
day14--postman接口测试
开篇-开启全新的.NET现代应用开发体验
Greenplum Database Fault Analysis - Why Does gpstart -a Return Failure After Version Upgrade?
使用OpenVINO实现飞桨版PGNet推理程序
(17) 51 MCU - AD/DA conversion
How to simply implement the quantization and compression of the model based on the OpenVINO POT tool
the mechanism of ideology
如何看待自己的羞愧感