
数据结构
文章平均质量分 71
数据结构基础
巧克力拌面
这个作者很懒,什么都没留下…
展开
-
1.1.数据结构绪论
数据元素用于描述一个个体。原创 2024-08-30 19:58:33 · 212 阅读 · 0 评论 -
1.2.数据结构的三要素以及数据类型和抽象数据类型
顺序存储链式存储索引存储散列存储注:顺序存储数据之间必须相邻。链式存储,索引存储和散列存储的数据之间是不相邻的,即离散,也叫非顺序存储或离散存储。如连续的数据可用顺序存储,找不到连续的数据可用非顺序存储如上述例子中在b和d之间插入c,对于顺序存储插入c的话要把大量元素进行移动空出位置后才能插入c,而非顺序存储只需要在b和d之间找一个空的地方插入c再修改指针指向的地址即可,效率比顺序存储高。原创 2024-08-30 19:56:49 · 704 阅读 · 0 评论 -
1.3.算法的基本概念
一个算法必须总在执行有穷步之后结束,且每一步都可在有穷时间内完成。注:算法(用有限步骤解决某个特定的问题)必须是有穷的,而程序可以是无穷或者有穷的如:微信是程序,但不是算法,因为微信在一直工作;死循环也不是算法;操作系统也不是算法,即使关机了,但后台操作系统仍在进行即无穷;算法中每条指令必须有确切的含义,对于相同的输入必须得出相同的输出。例如:把第一行年龄按照递增排序如果第一次输出为第二行的结果,第二次输出第三行的结果,那么这就不是算法,因为尽管年龄排序的结果一致,但名字不一样,要想是算法,就必须输出结果要原创 2024-09-07 11:03:54 · 409 阅读 · 0 评论 -
1.4.算法的时间复杂度
假设上述标记的5条语句执行时间相同(实际上时间不同,但考虑太多外在条件,就难以论述时间复杂度)语句频度:语句1-->只执行一次,因为顺序结构语句2-->执行了3001次,前3000次是符合i执行了3000次,因为只有进入循环才执行i++,循环了3000次语句4-->执行了3000次,因为只有进入循环才执行该语句,循环了3000次语句5-->只执行一次,因为顺原创 2024-08-31 21:42:17 · 604 阅读 · 0 评论 -
1.5.算法的空间复杂度
形参n和i各占4个字节,共8字节,是常数,可忽略。而数组flag所占字节是4 * n个,不是常数,不可忽略n为5和n为4时都有a,b,c的创建,只是地址不同,n为其他值时也一样。形参n和a,b,c都固定各占4个字节,共16个字节,所以每一次函数调用都大概需要16个字节来存储相应的信息即每一次aB个内存,而且共n次,刚好共占naB个内存数组flag所占字节和n有关,每一次函数调用n都会变化,因此flag所占字节每一次都会变化。原创 2024-08-31 21:46:19 · 375 阅读 · 0 评论 -
2.1.线性表的定义和基本操作
x=1024;printf("test函数内部的x=%d \n",x);printf("调用test函数前x=%d \n",x);test(x);printf("调用test函数后x=%d \n",x);return 0;/*运行结果为调用test函数前x=1test函数内部的x=1024调用test函数后x=1*//*解析:主函数里有一个x,值为1,第一条打印语句里x为1,原创 2024-09-07 10:59:14 · 191 阅读 · 0 评论 -
2.2.顺序表的定义
比如数组,数组大小一旦确定,就不可以再被改变。ElemType代表数据类型,比如整型,浮点型等。原创 2024-09-07 10:56:05 · 397 阅读 · 0 评论 -
2.3.顺序表的插入与删除
在b和d之间插入c,此时就需要把d,e,f都向后移一位,腾出一个位置后插入c。问题规模n=L.length(表长),当添加一个元素后,长度为n+1,所以在第一个位置添加元素时,要把前n个元素后移,空出第一个位置,此时长度为n+1。删除c后,后面的d,e,f都要前移一个,数组长度减一。ListDelete第三个参数有个&,这样就使得main函数里的e和ListDelete函数里的e是同一个e,不加&,main函数里的e和ListDelete函数里的e就不是同一个e了,执行完ListDelet原创 2024-09-07 10:46:54 · 890 阅读 · 0 评论 -
2.4.顺序表的查找
以顺序表的静态分配为例:例二:上述图片注意:data[0]后的data[1]不是紧跟着data[0],而是在占据data[0]该有的内存后才分配data[1](内存大小与所占字节有关,如整型占4个字节)malloc函数返回的存储空间起始地址要转换为与数据元素的数据类型相对应的指针,是因为虽然指针指向的都是同一个地址,但是如果指针所指向的数据类型给定义错了,那么在访问数据元素时也会出现问题。二.按位查找的时间复杂度:能实现"随机存取"是因为所有顺序表当中所有的数据元素在内存里面都是连续存放原创 2024-09-07 10:50:48 · 513 阅读 · 0 评论 -
2.5.单链表的定义
相比于顺序表,单链表不可随机存取,因此单链表中查找数据必须是一个一个找-->查找效率低单链表每一个节点有数据元素和存放该元素的指针,该指针指向下一个节点GetElem方法解读:把单链表L中的第i个节点取出来,并返回。参数列表中LinkList L也可以用LNode *L。LNode是一个结构体类型,LNode *就代表返回了一个指针,因为p为指针。LinkList是指针类型,用->关键词访问数据。方法体解读:LNode *p=L->next;代表L是指针指向下一个节点。原创 2024-09-07 10:40:57 · 317 阅读 · 0 评论 -
2.6.单链表的插入和删除
//定义单链表结点类型int data;//每个结点存放一个数据元素//指针指向下一个结点//初始化一个单链表(带头结点)//分配一个头结点if(L==NULL) //代表内存不足,分配失败-->意味着带头结点的单链表无法创建else//头结点之后暂时还没有节点,所以指向NULL//判断单链表是否为空(带头结点)if(L->next==NULL) //头结点之后如果指向NULL,代表没有数据else。原创 2024-09-10 00:04:27 · 2449 阅读 · 0 评论 -
2.7.单链表的查找与长度计算
版本二:王道书版本while循环进行到第5次时p指向NULL,不满足下一次循环条件,跳出while循环,此时返回的p为NULL:代表查找失败最终可知当i值不合法时即i为负数或者i值大于链表长度时最终都返回NULL,因此只需要判断返回结果是否为NULL即可得知是否查找成功。a.计算时间复杂度需要要查找的元素在合法范围内。b.平均时间复杂度是指此次输入的i值它取的合法范围内的任何一个数字的概率都等可能的情况,具体的算法和顺序表的按位查找的分析方法一样。案例二:后插操作的加入右下角的InsertNext原创 2024-09-13 00:12:58 · 818 阅读 · 0 评论 -
2.8.单链表的建立
对于时间复杂度,最好的时间复杂度是第一次,因为此时内层循环即找第i-1个结点就不执行了(不满足j原创 2024-09-13 00:02:47 · 2361 阅读 · 0 评论 -
2.9.双链表
三.双链表的插入:图解:此时要p结点之后插入s结点,起初p->next指向y,先把p的下一个结点即y和要插入的结点即s的指向下一个结点的指针对接即s->next = p->next:之后还需要把p结点的后继结点即p->next的前向指针p->next->prior指向s即p->next->prior = s:再把要插入的结点即s结点的前驱指针指向p结点即 s->prior = p:最后把p结点的后继结点指向s即p->next = s:但对于上述代码,有一个bug,当p结点是最后一个结点时,p->ne原创 2024-09-17 22:46:31 · 1026 阅读 · 0 评论 -
2.10.循环链表
1.对于普通带头结点的双链表而言,只有头结点时,它的后继指针和前驱指针都指向NULL;2.知道表为空时的状态就可以知道如何初始化;3.知道如何判断某个结点是否是表头/尾结点时,就可以知道如何前向遍历和后向遍历;4.注:所有链表都不具有随机访问的特性,因此找某个结点时只能通过循环查找,循环要么停在表头结点,要么停在表尾结点。原创 2024-12-13 20:28:10 · 378 阅读 · 0 评论 -
2.11.静态链表
define MaxSize 10 //定义静态链表的最大长度struct Node //定义静态链表结构类型int data;//存储数据元素int next;//下一个元素的数组下标//数组a作为静态链表(静态链表是连续的)int main()return 0;虽然静态链表是连续的存储空间,但在这一片连续的存储空间内各个逻辑上相邻的数据元素也可以在物理上不相邻,各个元素的关系是通过数组下标(游标)联系起来的。原创 2024-12-16 21:37:55 · 448 阅读 · 0 评论 -
2.12.顺序表和链表的比较
例如排队适合用链表,因为队伍长度无法预估;一个班级的人数可用顺序表,因为一个班的人数可准确得知;原创 2024-12-18 23:01:10 · 1012 阅读 · 0 评论 -
3.1.栈的基本概念
栈和线性表的区别:栈只能在表尾一端进行插入或者删除的操作,而线性表可以在任意一个地方进行插入或者删除。原创 2024-09-19 22:05:26 · 634 阅读 · 0 评论 -
3.2.栈的顺序存储实现
define MaxSize 10 //定义栈最多存入的元素个数//静态数组存放栈中元素int top;//栈顶指针} SqStack;//声明一个顺序栈(分配空间)//后续操作。。。return 0;原创 2024-09-19 22:58:10 · 624 阅读 · 0 评论 -
3.3.栈的链式存储实现
一.链栈的定义:二.总结:原创 2024-09-19 23:02:29 · 396 阅读 · 0 评论 -
3.4.队列的基本概念
空队列:队列中不含任何元素。原创 2024-09-28 12:04:05 · 246 阅读 · 0 评论 -
3.5.队列的顺序实现
可能会问,此时不是还剩一个存储空间吗,为什么认为满了呢,但需要注意的是,初始化队列时,队头指针和队尾指针是指向了同一个位置,因此判断是否队列已经满了,也需要看队头指针和队尾指针是否指向了同一个位置(注:队尾指针指向下一个插入元素的位置)#define MaxSize 10 //定义一个队列最多存储的元素个数//用静态数组存放队列元素int front;//队头指针,队头删除元素int rear;//队尾指针,队尾插入元素}SqQueue;//初始化队列//初始时,队头和队尾指针都指向0。原创 2024-09-28 12:13:12 · 1153 阅读 · 0 评论 -
3.6.队列的链式实现
二.初始化队列:1.带头结点:a.图解:2.不带头结点:a.图解:三.入队操作:1.带头结点:a.图解:2.不带头结点:a.图解:四.出队操作:1.带头结点:a.图解:不是最后一个元素出队:最后一个元素出队:2.不带头结点:a.图解:五.队列满的条件:从队头结点开始依次往后遍历,统计总共有多少个结点,显然时间复杂度为O(n)。原创 2024-09-28 12:26:44 · 632 阅读 · 0 评论 -
3.7.双端队列
双端队列也可以是只在一端删除和添加,此时就是栈;双端队列在一端添加,另一端输出,此时就是队列;共有24种排列方式:2,4,1,3不行,因为4出栈时说明3已经入栈了,下一个出栈的只能是3,而不是1。下图红色标注的输出序列都不合法:栈中合法的序列,双端队列中也一定合法。如果此时双端队列变为只能一端插入,两端删除时,栈中合法的序列在该双端队列中也一定合法,因此只需再验证栈中不合法的序列就可以得知哪些序列在该双端队列合法:比如4,3,2,1就不合法,因为4出去后,说明1,2,3已经进入,下一个出栈的要么是1(左边出原创 2024-10-01 22:40:46 · 703 阅读 · 0 评论 -
3.8.栈在括号匹配中的应用
把左括号依次压入栈中,越往后压入栈的左括号越先被匹配即被弹出栈->先进后出,后进先出当遇到左括号就压入栈中,当遇到右括号就把栈顶的左括号弹出,检查该右括号和左括号是否匹配当遇到一个不匹配的就停止压入栈,因为之后的括号必定都不匹配。其中Y代表Yes,N代表No1.对于bracketCheck函数,形参str[]是传入的一个字符串,length是该字符串的长度;字符topElem代表栈顶元素;return StackEmpty(S)如果是return false说明栈不为空,则左括号单身;创建栈顶元素topEl原创 2024-10-01 22:43:34 · 724 阅读 · 0 评论 -
3.9.栈在表达式求值中的应用
操作数:被操作的数或字符等;运算符:+,-,*,/,%等;界限符:()之类的界限符表明了各个运算符生效的顺序,如果没有界限符就会导致运算混乱1.注:例如后缀表达式:ab/和ba/是不一样的,ab/相当于a/b,ba/相当于b/a2.中缀表达式转换为后缀表达式:例一:a+b-c可以先算a+b即ab+,再算-c即ab+ c -(类似的,也可以先算b-c即bc-,再算a+即a bc- +)例二:a+b-c*d可以先算a+b即ab+,根据运算规则,需要算c * d即cd*,最终做差即ab+ cd * -3.中缀表达原创 2025-02-11 17:14:20 · 903 阅读 · 0 评论 -
3.10.栈在递归中的应用
1.main函数为主函数即程序入口,运行时主函数先入栈,然后存入主函数里的数据;2.func1函数加载在栈中时他后面的代码的地址#1(调用返回地址,不是下面其他代码的地址)也会被加载进去,其他函数也同理;3.func1里的a,b和主函数里的a,b是不一样的,因为加载的区域不一样导致地址也不一样,因此func1里传入a,b的值影响不到主函数里a,b的值;4.栈其实就是内存里的某一片区域(某一片存储空间);5.上图中main函数下还有代码,是因为在加载main函数前还会有一些未知的信息需要加载,原创 2024-10-01 22:48:27 · 615 阅读 · 0 评论 -
3.11.队列的应用
与5结点相邻的结点只有7,8结点没被处理过(6结点已经入队列了),因此只需7,8结点入队列,之后5结点出队列;同理处理2结点,与2相邻的1结点已经被处理过,因此无需入队,与2相邻的4结点没被处理过,4结点入队列,最后。之后的结点处理方式与上述同理,直到队列为空就完成了对图的广度优先遍历。遍历到4时,发现它没有子节点,所以4直接出队列即可,无需入子节点。原创 2024-10-01 22:51:02 · 485 阅读 · 0 评论 -
4.1.特殊矩阵的压缩存储
1.知道一维数组的起始地址,就可以求出任意下标对应的元素所在的地址;2.注:如果数组下标从1开始,上述公式的i就要改为i-1;3.数组里的元素类型相同,因此所占空间也相同。1.注:数组的存储是连续(计算机的存储是线性的)的,针对非线性的二维数组,为了将其拉成线性结构,就有了行优先和列优先两种存储方式;2.将非线性的二维数组整成线性的好处就是可以实现随机存取;3.行优先时计算存储地址:公式中i为行,j为列4.列优先时计算存储地址:公式中i为行,j为列由于对称矩阵关于主对角线对称,因此在存储对称矩阵的数据时只需原创 2024-10-01 23:02:49 · 1332 阅读 · 0 评论 -
4.2.串的定义和基本操作
1.单/双引号不是字符串里的内容,他只是一个边界符,用来表示字符串的头和尾;2.空串也是字符串的子串,空串长度为0;3.字符的编号是从1开始,不是0;4.空格也是字符;原创 2024-10-01 23:10:03 · 462 阅读 · 0 评论 -
4.3.串的存储结构
1.静态数组会由系统自动回收;动态数组需要手动回收;2.优点:随机存取,可以立即找到所需的字符;缺点:插入和删除较麻烦;3.串的顺序存储方案:对于方案二:如果ch[0]存储字符串长度,会导致字符串长度限制在0到255,因为ch[0]位置是一个字符型,占1B即8bit;对于方案三:额外设置一个变量记录长度,从头开始遍历,每找到一个字符,长度加一,当到\0时,停止遍历,得出字符串长度(如果经常要用到字符串长度的话,方案三的效率会低很多);对于方案四:ch[0]不存任何内容,从ch[1]开始存,使得字符的位序和数原创 2024-10-01 23:15:16 · 884 阅读 · 0 评论 -
4.4.朴素模式匹配算法
注:子串和模式串有区别。思路:在主串中找出所有与模式串长度相等的子串,与模式串进行比较,如果找到了,返回子串第一个字符在主串的位置思路:先在主串中找出所有与模式串长度相等的子串,设置两个指针,一开始分别指向主串的子串和模式串的第一个字符,这两个指针指到哪儿就把字符对比到哪儿,一开始比第一个字符,如果相等,这两个指针都后移继续比较下一个字符,不相等的话将主串中下一个与模式串长度相等的子串和模式串进行比较,以此类推:上述图片中j当前的值说明匹配到子串中的第几个字符,i-j让i的值回到目前子串的前面一个位置,因为原创 2024-10-08 22:47:45 · 733 阅读 · 0 评论 -
4.5.KMP算法(旧版上)-朴素模式匹配算法的优化
如果在j为5时匹配失败(j为模式串的指针),那么主串中分别以第5个字符即g,第6个字符即o,第7个字符即o开头的子串都不可能与模式串匹配,但第8个字符即g开头的子串有可能与模式串匹配成功,因为第8个字符即g能与模式串第一个字符g匹配上,而且在匹配过程中只是说明了子串中第9个字符与模式串中j为5时对应的字符不匹配,并没有说明子串中第9个字符与模式串中第二个字符不匹配,接下来。由于i和j指向的字符不匹配(不相等),根据j=next[j],会让j=next[1],此时j为0,下一轮循环时发现j为0,原创 2024-10-08 22:54:43 · 1213 阅读 · 0 评论 -
4.6.KMP算法(旧版下)-朴素模式匹配算法的优化
a.对于串的前缀,如模式串中第一个字符到第六个字符ababab,以ab为一段,第一个字符到第四个字符abab就是ababab的一个前缀,不包含最后一段;b.对于串的后缀,如主串中的第一个字符到第六个字符ababab,以ab为一段,第三个字符到第六个字符abab就是ababab的一个后缀,不包含第一段;c.注:前缀和后缀中前缀和后缀不一定只有一个字符组成,如ababab中a,ab,aba等都可以是它的前缀,后缀同理;原创 2024-10-08 23:15:07 · 975 阅读 · 0 评论 -
4.7.KMP算法(新版)
朴素模式匹配算法核心思想:把主串中所有长度与模式串长度相等的子串与模式串进行对比,直到找到第一个完全匹配的子串为止,如果当前尝试匹配的子串在某一个位置匹配失败,就立即进行下一个子串的匹配,而且是从下一个子串的头部开始一一匹配模式串的最后一个字符与主串中第一个和模式串长度相等的子串匹配失败:模式串与主串中第二个与模式串长度相等的子串比较,开始从2号字符开头的子串进行比较,之所以开始匹配2号字符开头的子串,且从2号字符开始比较,是因为在主串里的字符到底是什么无法得知,所以只能从主串里与模式串长度相等的子串的第一原创 2025-02-11 17:54:05 · 832 阅读 · 0 评论 -
5.1.树的定义和基本术语
1.祖先结点:从所指节点上方开始,沿一个方向一直到根结点之间(包括根结点)所遇到的结点都是祖先结点:如:结点"你"的祖先结点有"父亲"和"爷爷";2.子孙结点:从一个结点出发,他的所有分支以及他的分支的分支,一直到没有分支时的所有结点:如:结点"父亲"的子孙结点有"你","K","L"和"F";3.双亲结点(父结点/前驱结点):如结点"你"的双亲结点为"父亲";4.孩子结点(后继结点):如结点"你"的孩子结点为"K"和"L";原创 2024-10-08 23:20:34 · 339 阅读 · 0 评论 -
5.2.树的性质
一个分支中,如父结点B,两个子结点为E和F,结点B的度的值为2,等于子结点数量,加上这一个父结点(父结点只能有一个),就是结点B,E,F组成的树的总结点数即结点数 = 总度数 + 1高度为h的m叉树至少有h个结点(每层至少一个结点,共h层,所以至少共h个结点);高度为h,度为m的树至少有h+m-1个结点(首先度为m,表明至少要有m个分支,最少时一个分支上只有一个结点,此时有m个结点,这些分支占一层,此外有h-1层,每层最少1个结点,因此这h-1层至少h-1个结点,所以整个树至少h-1+m个结点)为了达到最小原创 2024-10-12 20:00:42 · 464 阅读 · 0 评论 -
5.3.二叉树的定义和基本术语
树是一种递归定义的数据结构,因此二叉树是递归定义的数据结构。注:a.满二叉树叶子节点的度为0,不存在度为1的结点;b.满二叉树的叶子节点都在最后一层中;c.上述图片的特点中的第三条最后一个是向下取整的符号,如[3/2]的值为1;d.满二叉树的每一层的结点都满了,不会再有额外的结点;e.完全二叉树:在满二叉树的基础上去掉一些或一个编号较大的结点,会导致其他结点的编号发生改变,如果改变后剩下的结点编号和未去掉结点的满二叉树的结点编号依旧一一对应,则为完全二叉树,反之不是;f.满二叉树是特殊的完全二叉树,完全二叉原创 2024-10-12 20:02:50 · 443 阅读 · 0 评论 -
5.4.二叉树的性质
推导过程:在完全二叉树中,n=a+b+c,由树的结点数=总度数+1,n = ( 0 * a + 1 * b + 2 * c ) + 1 = b+2c+1所以a = c +1 -> a+c = 2c+1 ,所以度为0的结点(叶子结点)总数和度为2的结点总数的和为奇数完全二叉树最多只能有一个度为1的结点,所以b只能为1(奇数)或者0(偶数因此,如果设n=2k为偶数,那么b只能为1,a为k,那么c为k-1如果设n=2k-1为奇数,那么b只能为0,a为k,那么c为k-1。原创 2024-10-12 20:06:09 · 632 阅读 · 0 评论 -
5.5.二叉树的存储结构
(i是结点编号)1.上述图片中i所在的层次后面的公式应该把n换成i(图片里写错了);2.上述图片判断i是否有左孩子,只需要判断是否2i[n/2] (这里是对n/2向下原创 2024-10-12 20:16:17 · 767 阅读 · 0 评论