数据结构之栈,队列,树

目录

一.栈

1.栈的概念及结构

2.栈的实现

3.实现讲解

1.初始化栈

2.销毁栈

3.压栈

4.出栈

5.返回栈顶元素

6.返回栈内元素个数

7.判断栈内是否为空

二.队列

1.队列的概念及结构

2.队列的实现

3.实现讲解

1.初始化队列

2.销毁队列

3.单个成员入队列

4.单个成员出队列

5.判断队列是否为空

6.返回队列内元素个数

7.返回队列首个元素

8.返回队列尾部元素

三.树

1.树的概念概念及结构

2.树的相关概念

3.树的实现

4.实现讲解

1.初始化树

2.销毁树

3.单个成员插入树

4.单个成员移除树

5.判断树是否为空

6.返回树中元素个数

7.返回树的顶部元素


———————————————————本文开始——————————————————

一.栈

1.栈的概念及结构

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

2.栈的实现

对于栈的实现我们可以使用数组或者是链表来完成,这里我们用数组来模拟实现一个栈:

3.实现讲解

对于栈,我们可以采用顺序表的形式来编写,首先是来对Stack结构体的定义,我们定义容量,栈顶数,以及数据指针

typedef int StackDataType;
typedef struct Stack
{StackDataType* data;int capacity;int top;
}Stack;

其次,我们想让栈具有以下几个功能,分别是:

1.初始化栈

2.销毁栈

3.压栈

4.出栈

5.返回栈顶元素

6.返回栈内元素个数

7.判断栈是否为空

下面我们分批次来完成这些函数功能:

1.初始化栈

我们先对栈来申请四个数据类型的空间,这里我们可以使用malloc函数,判断一下是否开辟成功,然后我们把开辟出的空间的地址赋值给栈内的data,将栈内的容量capacity赋值为开辟的数量4,将栈内的栈顶数置为0

void StackInit(Stack* q)
{assert(q);StackDataType* tmp = (StackDataType*)malloc(sizeof(StackDataType) * 4);if (tmp == NULL){perror("malloc failed");return;}q->data = tmp;q->capacity = 4;q->top = 0;
}

2.销毁栈

比较简单!我们直接释放掉栈内的data指针所指向的空间,将data置为空即可:

void StackDestroy(Stack* q)
{free(q->data);q->data = NULL;
}

3.压栈

将数据压入栈内,这里我们首先先来对栈内的容量和栈顶数做一个判断:

如果栈顶数等于栈的容量数,那么就说明此时栈已经满了,我们需要对栈进行扩容,

这里我们使用realloc函数,对栈内的data指针重新调整一块空间,这里我们同样简单判断一下空间是否开辟成功,并将栈内的容量数+4,代表我们扩容成功,

随后我们将要进行压栈的数据,放入data中,这里top就发挥作用了,可以让我们直接访问栈顶对应的空间,赋值即可,随后把top++代表放入数据成功:

void StackPush(Stack* q, StackDataType x)
{if (q->capacity == q->top){StackDataType* tmp = (StackDataType*)realloc(q->data, sizeof(StackDataType) * (q->capacity + 4));if (tmp == NULL){perror("realloc failed");return;}printf("extend successfully");q->data = tmp;q->capacity += 4;}q->data[q->top++] = x;
}

4.出栈

这里我们要使用另一个函数,判断栈内是否为空,如果为空,那么就没有数据可供pop出去了,我们结束这个函数,在自己撰写时,请记住提前声明函数哦,这样之后我们直接对栈内的top--即可:

void StackPop(Stack* q)
{assert(q);assert(!StackEmpty(q));q->top--;
}

5.返回栈顶元素

操作之前先来判断一下栈内是否为空,如果为空那么取栈顶数top-1就是错误访问数组了,

如果不为空,我们才返回data中的第top-1的元素:

STDataType StackTop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->a[ps->top-1];
}

6.返回栈内元素个数

这里不需要进别的操作,直接返回栈顶数top即可:

int StackSize(Stack* ps)
{return ps->top;
}

7.判断栈内是否为空

返回一个判断栈顶数top是否为0的表达式即可:

bool StackEmpty(Stack* ps)
{return ps->top == 0;
}

二.队列

1.队列的概念及结构

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

2.队列的实现

如果要实现一个队列,如果我们模仿栈使用一个顺序表,那么出队列的时候就很麻烦了!,因此我们在这里使用更为方便的链表实现队列的功能:

先来定义队列中每个成员节点的内容以及队列结构体中的首尾指针,哦对了!千万别忘记了添加一个int size成员,这关联到一个队列的重要功能!:

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

一个完整的队列应该具备以下几个功能:

1.初始化队列

2.销毁队列

3.单个成员入队列

4.单个成员出队列

5.判断队列是否为空

6.返回队列内元素个数

7.返回队列首个元素

8.返回队列尾部元素

3.实现讲解

1.初始化队列

这里我们传入一个Queue类型的指针,默认已经创建好了一个Queue结构体,我们只需使用指针把Queue内的tail和head头尾指针置为空,代表元素个数的size置为0,就完成了队列的初始化:

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

2.销毁队列

销毁队列难度不大,我们定义一个QNode*类型的指针cur,让cur从头指针head开始遍历整个队列,再定义另一个指针prev用来备份cur,判断条件就设置为cur!=NULL,然后让cur依次指向下一个节点,每次cur指向下一个节点之前都赋值给prev,再释放prev指向的空间,然后我们再把Queue结构体中的head,tail头尾指针置为空,size赋值为0即可:

void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->head;QNode* prev = NULL;while (cur){prev = cur;cur = cur->next;free(prev);}pq->head = NULL;pq->tail = NULL;pq->size = 0;
}

3.单个成员入队列

入队列,PushQueue,这里我们传入Queue*指针,以及要入队列的数据x,我们使用malloc先开辟一块QNode的空间来存放我们的数据,完成一个节点的创建,将newnode的next置为NULL,data赋值x,其次,将这一节点连接到队列当中去,这里其实类似于链表中的尾插,最后,别忘了让Queue中的size自加!

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

4.单个成员出队列

单个成员出队列,我们只需将head指针备份一下,将head重新赋值为下一个节点的地址,再释放备份的地址的空间,完成了出队列的功能,这里需要注意的是,当队列中只有一个成员时,出队列后head指向的是空,同时我们还需将tail改为空,否则在访问队列尾部成员的时候会对空指针进行非法解引用操作,并且要将size自减一个,代码如下:

void QueuePop(Queue* pq)
{assert(pq);assert(pq->head);QNode* del = pq->head;pq->head = pq->head->next;free(del);del = NULL;if (pq->head == NULL){pq->tail = NULL;}pq->size--;
}

5.判断队列是否为空

这里比较简单,我们直接返回判断size是否为0的表达式即可,值得一提的是,在前面出队列的时候,我们可以先用这一函数来判断队列是否为空,代替断言:

bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}

6.返回队列内元素个数

这里我们返回size就可以了,同样比较简单:

int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

7.返回队列首个元素

请注意,这里如果直接对head指针进行解引用,那么head可能为空,引发非法解引用的错误,因此,我们需要用到上面的函数,来判断队列是否为空,其次再返回头部成员:

QDatatype QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->head->data;
}

8.返回队列尾部元素

同样的道理,这里如果直接对tail指针进行解引用,那么tail可能为空,引发非法解引用的错误,因此,我们需要用到上面的函数,来判断队列是否为空,其次再返回尾部成员:

QDatatype QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}

三.树

1.树的概念概念及结构

树型结构是一类重要的非线性数据结构。其中以树和二叉树最为常用,直观看来,树是以分支关系定义的层次结构。把它叫做“树”是因为它常看起来像一棵倒挂的树,也就是说它常是根朝上,而叶朝下的。

2.树的相关概念

节点的度 一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
叶节点或终端节点 度为0的节点称为叶节点; 如上图:B、C、H、I...等节点为叶节点
非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G...等节点为分支节点
双亲节点或父节点 若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
孩子节点或子节点 一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
树的度:一棵树中, 最大的节点的度称为树的度; 如上图:树的度为6
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
子孙 以某节点为根的子树中任一节点都称为该节点的子孙 。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;

3.树的实现

树的实现可以采用链表实现,也可以采用顺序表也就是数组实现,这里我们只介绍数组实现树的方法,首先是对树这个结构体的定义问题,跟我们的顺序表相同,我们定义储存数据类型的指针,用来记录数据存放个数的size,以及后面用来放入数据判断是否需要扩容的capacity

typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;

一个完整的树应该具备以下几个功能:

1.初始化树

2.销毁树

3.单个成员插入树

4.单个成员移除树

5.判断树是否为空

6.返回树中元素个数

7.返回树的顶部元素

4.实现讲解

1.初始化树

初始化一个树,我们只需先使用malloc开辟一个初始设定值的空间来存放数据,并且,将结构体内部的capacity设定为初始值,size置为0即可,下面是代码实现:

void HeapInit(HP* php)
{assert(php);php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);if (php->a == NULL){perror("malloc failed");return;}php->size = 0;php->capacity = 4;
}

2.销毁树

比较简单,我们直接释放掉结构体内存放数据的指针a所指向空间即可,如果有必要的话可以将a置为NULL,防止野指针的产生,然后我们将capacity和size都置为0即可:

void HeapDestroy1(HP* php)//这里函数名后面加1是因为可能会与系统的函数冲突!
{free(php->a);php->a = NULL;free(php);	php = NULL;
}

3.单个成员插入树

这里首先要来判断树的容量是否足够大,也就是树中的capacity是否与size相等,如果相等那么就需要扩容,这里我们使用realloc重新申请一块capacity+增长值的空间,再重新赋给a,

同时capacity加上增长的值就完成了扩容,我们将要插入的数据放在最后,size++,到这里还没有结束,这也是树不同于前面链表与顺序表的地方,我们还需要对树进行调整

这里我选择的是建大堆,那么就是说父亲节点比孩子节点要大,我们插入一个AdjustUp调整树函数:

void HeapPush(HP* php, HPDataType x)
{if (php->capacity == php->size){HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * (php->capacity + 4));if (tmp == NULL){perror("realloc failed");return;}php->a = tmp;php->capacity += 4;}php->a[php->size++] = x;AdjustUp(php->a, php->size - 1);
}

下面是AdjustUp的实现:

对于这个函数,我们传入孩子节点以及要调整的数组的地址,定义父亲节点

这里需要用到一条规律:父亲节点=(孩子节点-1)/2;具体的推导过程可以参考别的讲解文章,不在此做过多介绍,将孩子节点的值与父亲节点比较,

如果前者大于后者,那么就进行交换,将父亲节点作为孩子节点继续判断,直到前者小于等于后者,就结束比较,外层我们使用一个while循环,条件设置为child>0,

为什么不是parent<0呢?经过计算发现,parent不可能小于0,最小就是0,因此最佳的解决方案是判断孩子节点的下坐标:

void swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child>0){if (a[child] > a[parent]){swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

4.单个成员移除树

首先判断树是否为空,如果不为空,就将数组的第一个值与最后一个值进行交换,因为这里Pop的是树的顶部元素,大堆顶部元素是最大的,我们进行交换后将size--,这样就访问间接删除了顶部元素,然后这里我们还需要向下建堆,AdjustDown函数:

void HeapPop(HP* php)
{assert(php);assert(!HeapEmpty(php));swap(&php->a[0], &php->a[php->size - 1]);php->size--;	AdjustDown(php->a, php->size, 0);
}

跟AdjustUp函数同理,对于这个函数,我们传入父亲节点以及要调整的数组的地址,还需要数组元素个数,定义孩子节点:

这里需要用到一条规律:孩子节点=父亲节点*2+1,这是公式父亲节点=(孩子节点-1)/2的逆推

这里我们需要选出孩子节点中的较大者来与父亲节点进行比较,如果前者大于后者,那么就进行交换,将孩子节点作为父亲节点继续判断,直到前者小于等于后者,就结束比较,外层我们使用一个while循环,条件设置为child<元素个数n:

void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){//选出最大的一个孩子if (child + 1 < n && a[child] < a[child + 1]){child = child + 1;}if (a[parent] < a[child]){swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}

5.判断树是否为空

返回结构体中size是否为0的判断表达式即可:

bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

6.返回树中元素个数

直接返回size的值即可:

int HeapSize1(HP* php)
{assert(php);return php->size;
}

7.返回树的顶部元素

这里需要判断树是否为空,在返回数组的第一个元素,也就是树的顶部元素:

HPDataType HeapTop(HP* php)
{assert(php);assert(!HeapEmpty(php));return php->a[0];
}

数据结构就更新到这里啦!觉得对你有帮助的话可以多多支持作者哦~

——————————————————本文结束——————————————————

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

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

相关文章

(六)循环神经网络_基本的RNN

一、提出背景 前馈神经网络不考虑数据之间的关联性&#xff0c;网络的输出只和当前时刻网络的输入相关。然而&#xff0c;现实问题中存在着很多序列型的数据&#xff08;文本、语音以及视频等&#xff09;。 例如&#xff1a;室外的温度是随着气候的变化而周期性的变化的&…

React引入Echart水球图

在搭建React项目时候&#xff0c;遇到了Echart官方文档中没有的水球图&#xff0c;此时该如何配置并将它显示到项目中呢&#xff1f; 目录 一、拓展网站 二、安装 三、React中引入 1、在components文件夹下新建一个组件 2、在组件中引入 3、使用水波球组件 一、拓展网站 …

微软edge浏览器 v131.0.2903.99便携版

前言 Microsoft Edge浏览器是个新浏览器&#xff0c;它用起来很简单&#xff0c;界面也很清爽。这个浏览器功能特别多&#xff0c;里面还带了微软的小助手Contana&#xff0c;能帮用户做不少贴心的事儿。它支持安装各种小工具&#xff08;插件&#xff09;&#xff0c;还能在网…

深度学习中batch_size

Batch size调整和epoch/iteration的关系 训练数据集总共有1000个样本。若batch_size10&#xff0c;那么训练完全体样本集需要100次迭代&#xff0c;1次epoch。 训练样本10000条&#xff0c;batchsize设置为20&#xff0c;将所有的训练样本在同一个模型中训练5遍&#xff0c;则…

使用“NodeMCU”、“红外模块”实现空调控制

项目思路 空调遥控器之所以能够实现对空调的控制&#xff0c;是因为它能够向空调发射出特定的红外信号。从理论上来说&#xff0c;任何能够发射出这种相同红外信号的红外发射器&#xff0c;都可以充当空调遥控器&#xff08;这也正是手机能够控制多种不同品牌空调的原因所在&a…

Git--tag标签远程管理

目录 一、git 标签 tag管理 1.创建一个轻量级标签 2.创建一个带有附注的标签 3.删除标签 二、标签推送 1.再创建两个分支 2.把多个标签推送到远程 三、标签拉取 四、删除远程标签 1.命令 2.查看远程仓库&#xff0c;标签被删除 3.远程标签删除后本地标签不会消失&a…

43. Three.js案例-绘制100个立方体

43. Three.js案例-绘制100个立方体 实现效果 知识点 WebGLRenderer&#xff08;WebGL渲染器&#xff09; WebGLRenderer是Three.js中最常用的渲染器之一&#xff0c;用于将3D场景渲染到网页上。 构造器 WebGLRenderer(parameters : Object) 参数类型描述parametersObject…

【论文阅读笔记】Scalable, Detailed and Mask-Free Universal Photometric Stereo

【论文阅读笔记】Scalable, Detailed and Mask-Free Universal Photometric Stereo 前言摘要引言Task 相关工作方法SDM-UniPS预处理尺度不变的空间光特征编码器像素采样变压器的非局部交互 PS-Mix数据集 实验结果训练细节评估和时间&#xff1a; 消融实验定向照明下的评估没有对…

【LuaFramework】服务器模块相关知识

目录 一、客户端代码 二、本地服务器代码 三、解决服务器无法多次接收客户端消息问题 一、客户端代码 连接本地服务器127.0.0.1:2012端口&#xff08;如何创本地服务器&#xff0c;放最后说&#xff09;&#xff0c;连接成功后会回调 协议号Connect是101&#xff0c;其他如下…

攻防世界 robots

开启场景 根据提示访问/robots.txt&#xff0c;发现了 f1ag_1s_h3re.php 拼接访问 /f1ag_1s_h3re.php 发现了 flag cyberpeace{d8b7025ed93ed79d44f64e94f2527a17}

gitlab克隆仓库报错fatal: unable to access ‘仓库地址xxxxxxxx‘

首次克隆仓库&#xff0c;失效了&#xff0c;上网查方法&#xff0c;都说是网络代理的问题&#xff0c;各种清理网络代理后都无效&#xff0c;去问同事&#xff1a; 先前都是直接复制的网页url当做远端url&#xff0c;或者点击按钮‘使用http克隆’ 这次对于我来说有效的远端u…

数字IC后端设计实现十大精华主题分享

今天小编给大家分享下吾爱IC社区星球上周十大后端精华主题。 Q1:星主&#xff0c;请教个问题&#xff0c;长tree的时候发现这个scan的tree 的skew差不多400p&#xff0c;我高亮了整个tree的schematic&#xff0c;我在想是不是我在这一系列mux前边打断&#xff0c;设置ignore p…

华为管理变革之道:管理制度创新

目录 华为崛起两大因素&#xff1a;管理制度创新和组织文化。 管理是科学&#xff0c;150年来管理史上最伟大的创新是流程 为什么要变革&#xff1f; 向世界标杆学习&#xff0c;是变革第一方法论 体系之一&#xff1a;华为的DSTE战略管理体系&#xff08;解决&#xff1a…

CentOS7下的vsftpd服务器和客户端

目录 1、安装vsftpd服务器和ftp客户端&#xff1b; 2、配置vsftpd服务器&#xff0c;允许普通用户登录、下载、上传文件&#xff1b; 3、配置vsftpd服务器&#xff0c;允许anonymous用户登录、下载、上传文件&#xff1b; 4、配置vsftpd服务器&#xff0c;允许root用户登录…

Python Polars快速入门指南:LazyFrames

前文已经介绍了Polars的Dataframe, Contexts 和 Expressions&#xff0c;本文继续介绍Polars的惰性API。惰性API是该库最强大的功能之一&#xff0c;使用惰性API可以设定一系列操作&#xff0c;而无需立即运行它们。相反&#xff0c;这些操作被保存为计算图&#xff0c;只在必要…

【论文阅读笔记】IC-Light

SCALING IN-THE-WILD TRAINING FOR DIFFUSION-BASED ILLUMINATION HARMONIZATION AND EDITING BY IMPOSING CONSISTENT LIGHT TRANSPORT 通过施加一致的光线传输来扩展基于扩散模型的真实场景光照协调与编辑训练 前言摘要引言相关工作基于学习的基于扩散模型的外观和光照操纵光…

学习threejs,THREE.CircleGeometry 二维平面圆形几何体

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.CircleGeometry 圆形…

AWS Certified AI Practitioner 自学考试心得

学习目标&#xff1a; 考取 AWS Certified AI Practitioner 那什么是 AWS Certified AI Practitioner 认证 是基础级的认证 比较简单 — 学习内容&#xff1a; 1. AWS网站自学网站 极客时间免费课程&#xff1a;http://gk.link/a/12sJL 配合极客时间课程的章节测试检验自…

WebRTC服务质量(07)- 重传机制(04) 接收NACK消息

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

RAGFlow 基于深度文档理解构建的开源 RAG引擎 - 安装部署

RAGFlow 基于深度文档理解构建的开源 RAG引擎 - 安装部署 flyfish 1. 确保 vm.max_map_count ≥ 262144 这是指要调整Linux内核参数vm.max_map_count&#xff0c;以确保其值至少为262144。这个参数控制着进程可以映射的最大内存区域数量。对于某些应用程序&#xff08;如Ela…