【初阶数据结构】栈和队列——C语言(详解)

目录

一、栈

1.1栈的概念及结构

1.2栈的实现

 1.2.1静态栈的实现

1.3动态栈的实现

1.3.1栈的创建

1.3.2栈的初始化

1.3.3栈的清空销毁

1.3.4栈的元素插入

1.3.5栈顶元素的删除

1.3.6返回栈顶数据

1.3.7求栈的大小

1.3.8判断栈是否为空

二、栈的实现完整代码

三、队列 

3.1队列的概念及结构

3.2队列的实现

3.2.1队列的创建

3.2.2队列的初始化

3.2.3队列内存的释放和销毁

3.2.4插入有效数据

3.2.5删除队列数据

3.2.6得到队头和队尾的数据

3.2.7判断是否为空

四、队列完整代码


一、栈

1.1栈的概念及结构

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

 

1.2栈的实现

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

 1.2.1静态栈的实现

实际中一般不用,我们主要使用支持动态增长的栈

typedef int STDataType;
#define N 10
typedef struct Stack
{
STDataType _a[N];
int _top; // 栈顶
}Stack;

1.3动态栈的实现

1.3.1栈的创建

typedef int STDatatype;
typedef struct STack
{STDatatype* a;int capacity;int top;
}ST;

这个结构和顺序表相似,a指向动态开辟的数组,capacity指的是动态开辟数组的容量,只不过这里的top指向的是栈顶也可以变相理解为栈里面的元素个数 。

1.3.2栈的初始化

void STInti(ST*st)
{st->a = NULL;st->capacity = 0;st->top = 0;//栈顶元素的下一个
}

当这里的top为0时我们插入一个元素,top会++,此时top可以取0或者1,但是我们已经插入一个数据了,因此top代表栈顶元素的下一个。

1.3.3栈的清空销毁

void STDer(ST* st)
{assert(st);free(st->a);st->capacity = st->top = 0;
}

首先就是要用assert判断是否传入栈了,如果传入栈就释放动态开辟的空间,再将容量置空,栈顶和栈底相融合。

1.3.4栈的元素插入

void STPush(ST* st,STDatatype x)
{if (st->capacity == st->top){int newcapacity = st->capacity == 0 ? 4 : st->capacity * 2;STDatatype* tmp = (STDatatype*)realloc(st->a, sizeof(STDatatype) * newcapacity);if (tmp == NULL){perror("realloc failed");exit(-1);}st->a = tmp;st->capacity = newcapacity;}st->a[st->top] = x;st->top++;
}

插入元素时我们要判断容量和栈顶是否融合,如果融合就要开辟新的空间,然后将想要插入的数据放入栈中,再将栈顶后移。

1.3.5栈顶元素的删除

void STPop(ST* st)
{assert(st);assert(st->top);st->top--;
}

 删除元素时不仅要判断是否有栈还要判断栈中的top是否为0,如果不为0,直接将top--就行;

1.3.6返回栈顶数据

STDatatype STTop(ST* st)
{assert(st);assert(st->top > 0);return st->a[st->top-1];
}

 返回栈顶数据top一定要大于0,当top为1时候返回的是top-1也就是0的值,然后top--此时top为0,当一开始的top为0时会造成越界,这点我们要注意。

1.3.7求栈的大小

int STSize(ST* st)
{assert(st);return st->top;
}

 初始化时我们将top置为0,当我们添加一个有效数据时top会++,这里的top也可以理解为栈的大小。

1.3.8判断栈是否为空

bool STEmpty(ST* st)
{assert(st);return st->top == 0;
}

当top为0时可以表示栈空,0等于0,返回bool值为true。

二、栈的实现完整代码

#define _CRT_SECURE_NO_WARNINGS 67
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDatatype;
typedef struct STack
{STDatatype* a;int capacity;int top;
}ST;
//初始化
void STInti(ST*st)
{st->a = NULL;st->capacity = 0;//栈顶元素的下一个st->top = 0;
}
//清空
void STDer(ST* st)
{assert(st);free(st->a);st->capacity = st->top = 0;
}
//插入
void STPush(ST* st,STDatatype x)
{if (st->capacity == st->top){int newcapacity = st->capacity == 0 ? 4 : st->capacity * 2;STDatatype* tmp = (STDatatype*)realloc(st->a, sizeof(STDatatype) * newcapacity);if (tmp == NULL){perror("realloc failed");exit(-1);}st->a = tmp;st->capacity = newcapacity;}st->a[st->top] = x;st->top++;
}
//删除
void STPop(ST* st)
{assert(st);assert(st->top);st->top--;
}
//打印d
STDatatype STTop(ST* st)
{assert(st);assert(st->top > 0);return st->a[st->top-1];
}
//求大小
int STSize(ST* st)
{assert(st);return st->top;
}
//判断栈是否为空
bool STEmpty(ST* st)
{assert(st);return st->top == 0;
}
int main()
{ST st;STInti(&st);STPush(&st, 1);STPush(&st, 2);STPush(&st, 3);STPush(&st, 4);STPush(&st, 5);while (!STEmpty(&st)){printf("%d ", STTop(&st));STPop(&st);}STDer(&st);return 0;
}

 

由于栈的特殊性我们在输出一个栈的数据时就要删除一个空间,刚开始top指向的是栈顶元素的下一个,打印有效数据是会减一也就是栈顶元素,然后删除top--指向刚才的栈顶元素的位置。

三、队列 

3.1队列的概念及结构

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

3.2队列的实现

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

 

3.2.1队列的创建

typedef int QDatatype;
typedef struct QueueNode
{struct Queue* next;QDatatype data;
}QNode;typedef struct Queue
{QNode* phead;QNode* tail;int size;
}Que;

这里我们使用的是无头单向非循环链表实现,但是队列的特点是先进先出,也就是对应链表中的尾插和头删,由于无头单项链表寻找尾部特别麻烦我们就在创建一个结构体里面放着指向链表结构头和尾的指针。

3.2.2队列的初始化

void QueueInit(Que* pq)
{assert(pq);pq->phead = pq->tail = NULL;pq->size = 0;
}

我们不能直接对队列进行初始化,只能对指向队列头和尾的两个指针初始化,将其置空。

3.2.3队列内存的释放和销毁

void QueueDer(Que* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}free(pq->phead);pq->phead = pq->tail = NULL;pq->size = 0;
}

根据指向头的指针找到队列的头依次往后释放内存,最后再将指向队列头和尾的指针置空。

3.2.4插入有效数据

void QueuePush(Que* pq,QDatatype x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->next = NULL;newnode->data = x;if ( pq->tail==NULL){pq->phead=pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}pq->size++;
}

插入数据是我们要特别注意指向队列尾的指针是否尾空如果尾空代表此时还没有队列,创建新的空间让队列的头和尾都指向这个空间,如果指向队列尾的指针不为空代表尾插,让指向尾的下一个指针指向新开辟的空间,再将指向尾的指针指向新的空间。

3.2.5删除队列数据

void QueuePop(Que* pq)
{assert(pq);assert(pq->phead);QNode* cur = pq->phead->next;free(pq->phead);pq->phead = cur;pq->size--;
}

这里我们就要判断指向队列头的指针是否尾空了,如果为空代表此时没有队列即没有数据可以删除,如果不为空创建一个指针让其指向队列头的下一个,然后释放队列的头,然后再将队列的头指向刚才创建的指针。

3.2.6得到队头和队尾的数据

//得到队头数据
QDatatype QueueFront(Que* pq)
{assert(pq);assert(pq->phead);return pq->phead->data;
}
//得到队尾数据
QDatatype QueueBack(Que* pq)
{assert(pq);assert(pq->phead);return pq->tail->data;
}

这里我们也要判断指向队列头的指针是否为空,如果为空代表此时没有队列,如果不为空则直接返回指向头或者尾所指向队列内部的有效数据。

3.2.7判断是否为空

bool QueueEmpty(Que* pq)
{assert(pq);return pq->phead == NULL;
}

当我们一边得到队头的数据时,会一边释放队头的空间,最后指向队列头的指针会被置空,返回的bool值为true。

四、队列完整代码

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QDatatype;
typedef struct QueueNode
{struct Queue* next;QDatatype data;
}QNode;typedef struct Queue
{QNode* phead;QNode* tail;int size;
}Que;
//初始化
void QueueInit(Que* pq)
{assert(pq);pq->phead = pq->tail = NULL;pq->size = 0;
}
//释放销毁内存
void QueueDer(Que* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}//free(pq->phead);pq->phead = pq->tail = NULL;pq->size = 0;
}
//插入数据
void QueuePush(Que* pq,QDatatype x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->next = NULL;newnode->data = x;if ( pq->tail==NULL){pq->phead=pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}pq->size++;
}
//删除数据
void QueuePop(Que* pq)
{assert(pq);assert(pq->phead);QNode* cur = pq->phead->next;free(pq->phead);pq->phead = cur;pq->size--;
}
//得到队头数据
QDatatype QueueFront(Que* pq)
{assert(pq);assert(pq->phead);return pq->phead->data;
}
//得到队尾数据
QDatatype QueueBack(Que* pq)
{assert(pq);assert(pq->phead);return pq->tail->data;
}
//得到个数
int QueueSize(Que* pq)
{assert(pq);return pq->size;
}
//判断是否为空
bool QueueEmpty(Que* pq)
{assert(pq);return pq->phead == NULL;
}
int main()
{Que q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueuePush(&q, 5);QueuePush(&q, 6);while (!QueueEmpty(&q)){printf("%d ", QueueFront(&q));QueuePop(&q);}printf("\n");QueueDer(&q);return 0;
}

 

栈和队列的相似之处是一边得到有效数据时会一边删除或者释放内存,所以我们在在打印有效数据时就会进行这样的操作。

C语言实现栈和队列的全部内容就讲解完了,欢迎大家在评论区多多讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/79953.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity 动画系统

动画系统包含&#xff1a; 动画片段 Animation Clip&#xff0c;记录物体变化的信息&#xff0c;可以是角色的闪转腾挪&#xff0c;也可以是一扇门的开闭动画状态机 Animator Controller&#xff0c;根据设置切换动画片段动画组件 Animator&#xff0c;Animation替身 Avatar&a…

数据结构基础8:二叉树oj+层序遍历。

二叉树oj层序遍历 题目一&#xff1a;二叉树的销毁&#xff1a;方法一&#xff1a;前序遍历&#xff1a;方法二&#xff1a;后序遍历&#xff1a; 题目二&#xff1a;二叉树查找值为x的节点方法一&#xff1a;方法二&#xff1a;方法三&#xff1a; 题目三&#xff1a;层序遍历…

透视俄乌网络战之二:Conti勒索软件集团(下)

透视俄乌网络战之一&#xff1a;数据擦除软件 透视俄乌网络战之二&#xff1a;Conti勒索软件集团&#xff08;上&#xff09; Conti勒索软件集团&#xff08;下&#xff09; 1. 管理面板源代码2. Pony凭证窃取恶意软件3. TTPs4. Conti Locker v2源代码5. Conti团伙培训材料6. T…

【Linux从入门到精通】多线程 | 线程互斥(互斥锁)

上篇文章我们对线程 | 线程介绍&线程控制介绍后&#xff0c;本篇文章将会对多线程中的线程互斥与互斥锁的概念进行详解。同时结合实际例子解释了可重入与不被重入函数、临界资源与临界区和原子性的概念。希望本篇文章会对你有所帮助。 文章目录 引入 一、重入与临界 1、1 可…

多线程|多进程|高并发网络编程

一.多进程并发服务器 多进程并发服务器是一种经典的服务器架构&#xff0c;它通过创建多个子进程来处理客户端连接&#xff0c;从而实现并发处理多个客户端请求的能力。 概念&#xff1a; 服务器启动时&#xff0c;创建主进程&#xff0c;并绑定监听端口。当有客户端连接请求…

GitLab使用的最简便方式

GitLab介绍 GitLab是一个基于Git版本控制系统的开源平台&#xff0c;用于代码托管&#xff0c;持续集成&#xff0c;以及协作开发。它提供了一套完整的工具&#xff0c;以帮助开发团队协同工作、管理和部署代码。 往往在企业内部使用gitlab管理代码&#xff0c;记录一下将本地代…

VR航天航空巡展VR科技馆航天主题科普设备沉浸遨游太空

每当飞机飞过头顶&#xff0c;我们总是忍不住抬头去仰望。从嫦娥奔月的神话传说&#xff0c;到莱特兄弟实现了上天翱翔的梦想&#xff0c;人类一直在不断探索更辽阔的天空和浩瀚的宇宙。 航空科普 寻梦而行 普乐蛙VR航天航空巡展&#xff0c;正在湖南郴州如火如荼的进行中&…

arm day2(9.15)数据操作指令,跳转指令,特殊功能寄存器指令,

作业 1.求最大公约数&#xff1a; .text .global _start _start:mov r0,#0x9mov r1,#0x15bl Loop Loop:cmp r0,r1 比较r0寄存器和r1寄存器的中的值beq stop 当两数相同时,退出程序subhi r0,r0,r1 r0>r1 r0 r0 - r1subcc r1,r1,r0 r0<r1 r1 r1 - r0mov pc,lr 恢复现…

【算法与数据结构】108、LeetCode将有序数组转换为二叉搜索树

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;这道题给我们的是一个有序数组&#xff0c;并要求构成一个平衡二叉搜索树&#xff0c;二叉搜索树的很容…

CSS 之 grid 网格布局

一、简介 ​ display: grid;用于设置元素内部的布局类型为网格布局&#xff0c;其外显类型为块级元素。该类型的元素将内部分为行和列&#xff0c;划分成一个个单元格&#xff0c;并通过一系列相关属性控制单元格及其内容的布局和大小。 ​ 该属性值的主要应用场景为&#xf…

这个锂电池保护方案来自TIDA-010030

本篇博客只是作为个人记录&#xff0c;拆锂电池有危险&#xff0c;撬棒刺穿外壳可能爆炸&#xff0c;请勿模仿&#xff0c;误操作电池数据可能失效&#xff0c;请勿模仿。 1、简介 1.1、目的 得到该电池的电量计芯片型号、IIC从机地址、通信的实际波形&#xff1b; 1.2、步…

百度飞浆OCR识别表格入门python实践

1. 百度飞桨&#xff08;PaddlePaddle&#xff09; 百度飞桨&#xff08;PaddlePaddle&#xff09;是百度推出的一款深度学习平台&#xff0c;旨在为开发者提供强大的深度学习框架和工具。飞桨提供了包括OCR&#xff08;光学字符识别&#xff09;在内的多种功能&#xff0c;可…

Linux驱动IO篇——异步通知

文章目录 什么是异步通知异步通知和异步IO的区别信号含义应用层使用信号驱动如何实现异步信号驱动实例 什么是异步通知 异步通知在Linux的实现中是通过信号&#xff0c;而信号是在软件层次上对中断机制的一种模拟。这种机制和中断非常类似&#xff0c;所以可以以中断的思想来理…

重新认识交叉编译

1. 我以前对交叉编译的认知 引用正点原子的话来讲就是: 说得对&#xff0c;但是不全面&#xff0c;直到最近项目中遇到了一个例子我才重新认识什么是交叉编译。 2. build/host/target的概念 参考: Cross-Compilation (automake) 参考: Specifying Target Triplets (Autocon…

Python二级 每周练习题18

练习一: 从键盘输入任意字符串&#xff0c;按照下面要求分离字符串中的字符: 1、分别取出该字符串的第偶数位的元素(提醒注意:是按照从左往右数的方式确定字符串的位置) 2、并依次存储到一个列表中; 3、输出这个列表。 答案: ninput(请输入任意字符串:) #创建变量n存放用户…

数据结构入门 — 树的概念与结构

本文属于数据结构专栏文章&#xff0c;适合数据结构入门者学习&#xff0c;涵盖数据结构基础的知识和内容体系&#xff0c;文章在介绍数据结构时会配合上动图演示&#xff0c;方便初学者在学习数据结构时理解和学习&#xff0c;了解数据结构系列专栏点击下方链接。 博客主页&am…

外包干了2个月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

【多线程】Thread 类 详解

Thread 类 详解 一. 创建线程1. 继承 Thread 类2. 实现 Runnable 接口3. 其他变形4. 多线程的优势-增加运行速度 二. Thread 类1. 构造方法2. 常见属性3. 启动线程-start()4. 中断线程-interrupt()5. 线程等待-join()6. 线程休眠-sleep()7. 获取当前线程引用 三. 线程的状态1. …

Buffer Pool

一.Buffer Pool的含义 Buffer Pool&#xff1a;缓冲池&#xff0c;简称BP&#xff0c;其作用是用来缓存表数据与索引数据&#xff0c;减少磁盘IO操作&#xff0c;提升效率。当Mysql执行查询的sql语句的时候&#xff0c;会先去缓存当中看是否有对应的数据&#xff0c;如果有则直…

显示器显示的画面突然偏红色如何解决

显示器显示的画面突然偏红色如何解决 1. 概述2. 解决方法结束语 1. 概述 显示器显示的画面突然偏红色 &#xff0c;使用向日葵远程电脑&#xff0c;看到的画面是正常的&#xff0c;但是显示器上的画面确还是骗红的&#xff0c;这时候就需要看一下是不是开启了系统也夜间模式&a…