那么关于图,我将从以下几点进行总结: 1、图的定义 2、图相关的概念和术语 3、图的创建和遍历 1、图的定义 什么是图呢? 图是一种复杂的非线性结构。 父节点)及下一层的多个元素(孩子节点)相关; 而在图形结构中,节点之间的关系是任意的,图中任意两个数据元素之间都有可能相关。 同理,将具有n(n-1)条边的有向图称为有向完全图。 2-3、顶点的度 对于无向图,顶点的度表示以该顶点作为一个端点的边的数目。 比如,图(a)无向图中顶点V3的度D(V3)=3 对于有向图,顶点的度分为入度和出度。入度表示以该顶点为终点的入边数目,出度是以该顶点为起点的出边数目,该顶点的度等于其入度和出度之和。 下面是一个非连通图的例子: ? 上图中,因为V5和V6是单独的,所以是非连通图。 2-7、强连通图(有向图) 强连通图是对于有向图而言的,与无向图的连通图类似。 2-8、网 带”权值”的连通图称为网。
前一篇文章我们一起实现了自定义的set集合类。那么这一篇我们来给set类增加一些操作方法。那么在开始之前,还是有必要解释一下集合的操作有哪些。便于我们更快速的理解代码。 1、并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。注意,集合中不会有重复的值。 2、交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。 这里我们就不详细的再赘述一遍集合操作的数学计算方法了。有兴趣或者忘记了的小伙伴可以百度一下。那么咱们就正式开始集合的操作方法。 中的值存入新的unionSet中就可以了。 只是要注意的是如果setA的子集是setB,那么setA的元素个数是一定大于或等于setB的。
大家好,又见面了,我是你们的朋友全栈君。 前一篇文章我们一起实现了自定义的set集合类。那么这一篇我们来给set类增加一些操作方法。那么在开始之前,还是有必要解释一下集合的操作有哪些。 便于我们更快速的理解代码。 1、并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合。注意,集合中不会有重复的值。 2、交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合。 3、差集:对于给定的集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合。 4、验证一个给定集合是否是另一个集合的子集。 这里我们就不详细的再赘述一遍集合操作的数学计算方法了。有兴趣或者忘记了的小伙伴可以百度一下。那么咱们就正式开始集合的操作方法。 回顾一下代码,我们发现其实集合的各种操作方法在我们的实际工作中也是经常应用到的,只是我们在用数组操作,并没有十分的去注意这些细节。比如并集操作,我们在合并两个数组的时候肯定用到过。
队列 队列的特性是先进先出。每次数据出去只能的队列的头部,每次数据进来只能加在队列的尾部。 队列实现一般有两种方式,线性队列,链表队列。 链表队列 链表队列的实现可以参考单向链表。 线性队列 超简单的,基于数组实现,每次删除数据则把数组第一个删除,把后续的往前面移动,最后一个直接置空;添加数据只需要在最后继续添加即可;数组会有定长,删除和添加数据一定要检验。 #include <iostream> using namespace std; typedef struct queue { int data[10]; int length; int rear; } queue; queue add_num(queue q, int num) { if (q.length + 1 <= 10) { q.data [q.length] = num; q.length += 1; } else if (q.length + 1 > 10) { cout <<
1.栈的概念及结构 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈的删除操作叫做出栈。出数据也在栈顶。 2.栈的实现 栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。 0,则相当于是malloc,扩容完之后就将数据放进top这个位置,然后再将top++,这样才会使得top一直是栈顶元素的下一个位置。 void STPop(ST* ps, STDataType x) { assert(ps); //空 assert(ps->top > 0); --ps->top; } 2.6栈的数据个数 int
前言 在上一篇关于树和二叉树的博客中,最后提到了堆。有小根堆和大根堆。 左边的结构是我们想象出来的,右边才是实际存储的结构。 这次来实现堆。 2. 堆的实现 用数组来实现,这里以实现小堆为例子,它的特点是父节点小于子节点。 先定义一个堆的结构体:为了方便扩容,加了size。 2.2.1.3 情况三 这里先将10与它的父节点28比较,发现10比较小,此时就交换它们两个;再往上看,发现18比10小,又交换;再往上,发现15也比10小,此时又交换。 2.2.2 插入代码实现 先判断空间是否足够,不够就扩容,够就直接插入x,再将php->size++。 2.3.2 删除代码实现 首尾交换删除,然后将php->size--,最后向下调整。
链表的概念 概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。 这里我来解释一下什么叫做逻辑上连续而物理上补连续。 所谓逻辑上的连续,就是指你你可以通过第一个节点找到第二个节点,就行火车一样你可以从一节车厢走到另一节的车厢。 物理上的不连续是指内存地址的不连续,不想顺序表中的数组是连续的。 next就起到了重要的作用,我们把2的地址存在1的next中,把3的地址存在2的next中,最后把3的next指向NULL就可以了,再画一个图就是这样的。 我还用了一个头节点来指向第一个节点,从图上来看是不是每个节点都联系了起来,这就是有了逻辑上的连续。 下面我来讲解实现链表都要哪些函数。 头插函数 想要实现头部插入,我们先画图。 要想吧新的节点插入头部,我们就要把,新节点的nest指向第一个节点,然后把pphead指向新的节点。
之前我们学习过数据结构中的栈和队列,详情可点击这里数据结构——lesson5栈和队列详解进行查看,队列是一种先进先出的结构,但是我们之前讲的队列都是类似于线性的物理结构,这次我们所介绍的队列则是一直类似于环状的循环结构 1.循环队列的介绍 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 rear的下一个元素指向front,如果增加一个空闲的位置,队列满时rear的下一个位置就不再指向front; 在决定选哪种方法之前,我们先要考虑一下是使用链表来实现还是使用数组也就是顺序表来实现循环队列 ;当然这里土土会将两种方法都写下来,并和大家一起分析两种方法的优劣之处,以便大家选择合适和喜欢的形式(对于顺序表链表有疑问的可以在土土的数据结构专栏里——数据结构学习笔记 进行查看复习哦~) 3.用单链表实现循环队列 ,对应数组实现循环队列则需要front,rear不断进行取模运算以防越界; 但是链表实现需要手动将开辟的节点链接在一起,数组则不一样它一开辟就是地址连续的一段空间; 其他的实现链表和数组都差不多;
之前编写了自己的数组,下来基于之前的基础之上实现了栈的基本内容
namespace DataStructure
{
class Program
{
///
这时我们就可以使用另一种数据结构--链表。 和顺序表一样,链表也是一种线性表。和顺序表不同的是,虽然链表在逻辑上是连续的,链表在物理上并不是连续的。 链表中的节点通过指针域相互连接,形成一个动态的数据结构,可以方便地进行插入、删除等操作,而不需要像数组一样需要连续的内存空间。 单链表的实现 模块划分 和实现顺序表一样,分为三个文件来实现,SList..c用来实现顺序表的各种方法,SList.h用来包含实现方法所需的头文件和所需方法的初始化。 test.c用来测试写的方法是否有问题。 节点 实现链表的节点需要创建两个变量,数据域用来储存数据,指针域用来存放下一个节点的地址。我们用结构体来实现。 = NULL) { printf("找到了\n"); } else { printf("没找到\n"); } SLTNode* find2 = SLTFind(plist, 10); if (find2
1.线性表 线性表(linear list)是n个具有相同特性的数据元素的有线序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就是说是连续的一条线。但是在物理结构上并不一定是连续的,比如链表。 * a; int sz;//有效数据个数 int capacity;//存储空间大小 }SL; 3.模拟实现 静态顺序表只适合于确定知道要存储多少数据的场景下。 下面是模拟实现: 3.1 准备工作 定义一个结构体 typedef int Datatype;//为了适用不同类型的顺序表 typedef struct SeqList { Datatype* a; 同时还要删除该顺序表中的数据也又两种情况: 1.顺序表中的数据已经删完了,无法再删。 2.顺序表中的数据足够删除。
上一次我们实现了顺序表的应用,但是对于顺序表还是会有以下几个问题: 1. 中间/头部的插入删除,时间复杂度为O(N)。 2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 3. 单链表的概念 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。单链表中的数据元素由一个数据域和一个指针域组成,其中指针域指向下一个数据元素。 注意:每次申请一块节点空间时,它和之前的节点空间不一定是连续的,所以要想找到这块空间就得通过地址来找。 链表一共有8种分类,如下图所示: 今天我们来实现单链表,也就是不带头单向不循环链表。 同顺序表一样我们先创建一个SList.h的头文件和一个SList.c的源文件,.h文件实现函数的声明,.c文件实现函数的定义。 现在我们来实现尾插,如下图: 尾插很简单,我们只需要注意,循环遍历时结束条件是尾节点的next指针为空,而不是尾节点为空。
上一次我们实现了单链表,也就是不带头单向不循环链表,这次我们首先双链表(带头双向循环链表)。 对于单链表来说,还是存在一些缺点的,例如:查找效率低(需要从头开始遍历链表),不能随机访问。 在List.h文件中,首先创建一个结构体用来存放节点的信息,这里和单链表的区别是我们额外增加了一个结构体指针成员prev用来指向上一个节点的指针。 接下来我们来实现双链表的增删查改操作。 所以我们要养成良好的编程习惯。 我们创建一个test.c文件调试一下: 2.链表的尾插 双链表尾插实现就很简单了,不同于单链表还要先找到尾节点,这里phead->prev指向的就是尾节点。 为了方便测试我们先将打印方法实现。 3.链表的打印 打印就需要循环遍历一遍,注意我们的条件现在不再是pcur->next != NULL,而是!= phead。 10.链表的销毁 只需要循环遍历链表,将所有节点释放就行。 但需要注意的是,我们在释放前要先将下一个节点的地址存在next中,不然在我们释放掉pcur之后,拿不到下一个节点的地址。
堆的实现 介绍的话就到此为止,下面我们来进行堆的实现。无非就是那几样。 ,没错就是顺序表,但实现起来会比顺序表更难一些哦。 函数的声明 我们先把初始化,销毁,插入,删除等等要实现的函数声明一下: void HpInit(HP* php); void Hppush(HP* php, HpDataType x); void Hpdestroy 这里我们插入40,40比56小那么它应该是“父亲”,所以我们就要把他和56调换位置,但是如果我们再多想一想,我插入的是9呢,是不是就还要和10调换一下位置了?对的,这就是小堆的内涵,小的永远再上面。 我们发现下面我有写了有个函数,向上调整的函数adjustup,没错上面我们讲的谁小于谁交换就要通过这个函数来实现。
栈满足的特性是先进后出,就像货车装货物,把货物一次放进去,但是卸货的时候,你得先把最外面的卸载了,才能继续卸载里层的货物。 栈的实现有两种形式,一种是数组,一种是链表。 ? 数组栈的长度一定,使用top记录当前的位置,比如说,array长度为10,但是我们指存储了5个数据,那么top的值就是4。往栈里面加入一个数据,top变成5,当已经10个数据时,top为9。 关于实现一个顺序栈 #include <stdio.h> #include <iostream> #include <stdlib.h> using namespace std; class Stack { public: int top; int length; int content[10]; int push(int num); int pop(); ; }; int Stack::peek() { return content[top]; }; int Stack::push(int num){{top += 1; if (top >= 10
目录 前言 堆的概念和结构 堆的实现 接口展示 堆结构创建 堆的初始化 堆的销毁 入堆 数据向上调整 入堆测试 出堆 向下调整数据 出堆测试 堆顶数据获取 堆数据个数 判断空堆 堆数据打印 堆源码 ---- 前言 ---- 本章主要讲解: 数据结构中的堆的知识以及实现 堆的概念和结构 ---- 概念: 将所有元素按完全二叉树的顺序存储方式存储在一个一维数组中并以一定的数据要求存储 堆总是一棵完全二叉树 注:在上节基础知识讲解中我们知道父节点和左右子节点的编码关系,以此可以对应到数组中的下标关系,这里我们主要用数组来表示和实现堆 图示:数组堆 堆的实现 ---- 注:这里我们主要实现大堆,对于小堆的实现与大堆只有数据调整上的出入 接口展示 //堆初始化 void HeapInit(HP* hp); //堆销毁 void HeapDestroy(HP* hp) = parent; parent = (child - 1) / 2; } } 入堆测试 测试代码: int main() { int a[] = { 70, 56, 30, 25, 15, 10
数据结构 合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下 ——老子 1 每日一练 1.评价一个好的算法,您是从哪几方面来考虑的? 2.解释和比较以下各组概念 (1)算法的时间复杂性 (2)算法 (3)频度 3. 根据数据元素之间的逻辑关系,一般有哪几类基本的数据结构? 4.对于一个数据结构,一般包括哪三个方面的讨论? 5. 当你为解决某一问题而选择数据结构时,应从哪些方面考虑? 6. 若将数据结构定义为一个二元组(D,R),说明符号 D,R 应分别表示什么? 正确答案 1.评价好的算法有四个方面。一是算法的正确性;二是算法的易读性;三是算法的健壮性;四是算法的时空效率(运行)。 2.(1)算法的时间复杂性是算法输入规模的函数。 算法的输入规模或问题的规模是作为该算法输入的数据所含 数据元素的数目,或与此数目有关的其它参数。有时考虑算法在最坏情况下的时间复杂度或平均时间复杂度。
(为了简单起见,假设表中不存在重复数据项) 如一个考试分数的集合“54,26,93,1777和31” 如果用无序表来表示,就是[54,26,9317, 77, 31] 采用链表实现无序表 采用链表实现无序表的主要原因是 另外,链表实现无序表还有以下优点: 内存使用效率高:链表只需要分配和使用存储空间,而不需要事先设置固定的存储大小,这可以节省内存空间。 链表是一种非常适合实现无序表的数据结构,因为它具有动态性,灵活性,高效性和内存使用效率高等优点。 可以采用 链接结点 的方式来构建数据集 实现无序表 链表的第一个 和 最后一个 节点最重要 如果想访问到链表中的所有节点,就必须从第一个节点沿链接遍历下去. Unordered List - 无序表 箭头所指为表头 最快捷的就是从表头开始(相当于insert[0]), 但是之前列表实现inser[0]的时间复杂度是O(n), 而链表是O(1) 结点(node
数据结构与算法 : 一.数据结构和算法简介 数据结构是指数据在计算机存储空间中的安排方式,而算法时值软件程序用来操作这些结构中的数据的过程. 二. 在这个例子中数组就是一个数据结构,而使用for循环来遍历数组,则是一个简单的算法.可见数据结构和算法是构成程序的灵魂所在,而且也有人提出数据结构+算法=程序. 数据结构 = 逻辑结构 + 存储结构 + (在存储结构上的)运算/操作; 数据的逻辑结构 数据的逻辑结构指数据元素之间的逻辑关系(和实现无关). 表和树是最常用的两种高效数据结构,许多高效的算法能够用这两个数据结构来设计实现. 线性结构 : 数据结构中线性结构值得是数据元素之间存在着"一对一"的线性关系的数据结构. 一个带头结点的单链表实现线性表的结构图如图所示.
: 栈实际上也是一个存储数据的结构,和我们的线性表是一样的,但是栈肯定是有自己的独特之处的,否则也不会作为一个单独的数据结构存在,确实,栈的特殊性就在于我们的数据都是从栈顶进入,栈顶出去,这个就是栈的特殊之处 拿这个{(【】)}作为案例,我们的{先进栈,(再进栈,【再进栈,这个时候我们的左边的括号都判断完了,我们需要判断右边的符号和左边的是不是一一对应的,这个时候就可以使用栈这个数据结构,因为我们的栈是后进先出 ,这个时候栈这个数据结构就可以帮助我们判断这个一串符号前后是不是一一对应的。 2.栈的实现 我们还是写一个系统的文件,包括栈的实现的头文件,源文件和测试文件这三个文件; 我们在头文件里面进行相关函数的声明,源文件里面进行相关的函数的定义,测试文件里面定义一个栈进行我们写的函数的测试 5.栈的应用--有效的括号 这里的应用就是我们开始提到的括号的匹配问题,我们要判断这个括号之间是否是相互匹配的; 因为现阶段我们还是使用C语言进行实现,所以我们要自己实现栈,如果是C++,我们可以使用相应的一些库