数据结构
文章平均质量分 60
数据结构的设计实现和典型题目
wzdxsa
大大方方的去肯定自己
展开
-
BF算法(2)
不直接把strlen计算写到循环里是因为这样就不用每一次进入循环都要计算一遍了。上面的if判断里面写了strlen是因为那里没有定义循环,if里也只计算一遍。下面来看看BF算法的时间复杂度:O(n*m)(主串长度乘子串长度)接下来定义主串下标i和子串下标j,开始比较是否相等并判断比较条件。判断条件里要求str和sub的长度,在前面加上计算长度,一进入函数先参数判断,考虑每个参数成员一遍。循环内是相等继续和不相等回退2种情况,全部遍历完后,出循环要判断是否查找成功。先定义2个串,主串和子串。原创 2024-03-05 08:26:29 · 366 阅读 · 0 评论 -
串的定义及BF算法
用来搜索关键字,而Search函数只是用来搜索一个字符,比如下面push关键字的开头p,但search搜索不了整个关键字push。因为如果i不回退,那就只找了主串中以第一个字符开头的子串,第二个到第n个字符开头的子串都没找,漏掉了怎么能说没找到。其应用特别多,比如经常在一篇文章里面搜索一些东西,(比如文章里的某个内容,或某些关键字词出现的位置,次数等)主串的下标i,和子串的下标j,然后将i位置的字符和j位置的字符进行对比,2个都一样就一块往后走,比下一个。每次不匹配时,i回退到上次开始的位置的下一个;原创 2024-03-04 22:57:25 · 359 阅读 · 0 评论 -
静态链表(3)
综上所述,静态链表就是处理两条链表,静态链表总的执行一次插入或删除,就是分开的2条各执行一次插入或删除,而分开的链表里面执行的插入或删除操作步骤跟前面的链表是一模一样的。因为该静态链表只有8个数据域可以放数据,所以如果在头插之后接着尾插,它并没有地方放数据了,所以输出的还跟之前的头插一样。综上所述,尾插就是,获取——剔除——放数据——找尾巴——插入。例如,静态链总插入,则有效链插入,空闲链删除;静态链总删除,则有效链删除,空闲链插入;尾插就比头插多了一步找尾巴,其他均一样。所以将头插注释掉换成尾插。原创 2024-03-04 08:01:33 · 293 阅读 · 0 评论 -
静态链表(1)
如果已知有一个空闲结点的情况下是遍历一次(所以有的说On),但若是有5000个结点,且不知道里面一共有几个空闲结点时,且第几个为空闲结点时,那就每个结点都需要遍历一遍看看在不在里面了。而在静态链表里面,要想插入,只要找到空闲结点O(n^2),将要插入的有效数据val赋值覆掉原来的无效数据O1,盖然后把它接入(先绑后,再接前)到有效数据链里面O1,就完成了。(a)图中0的next是1,1的next是2,……之前说过数组中的数据域都是有数字的,只不过有些数字有效,有些数字无效,无效的数字在数组中就当做不存在。原创 2024-02-28 17:07:53 · 365 阅读 · 0 评论 -
双向链表(2)
但在双向链表里面,前驱已经用prio保存了(在找到Search的结点里面的prio),所以不用调用函数了。p->prio则不可能为空,因为它最多可能在下图中的位置,prio也是plist的位置100。虽然第二条链没断,但在free(p)之后,整个结点p都没了,此时第二条链自然就断了。将第一行中的300(p->prio->next)改成200(p->next),销毁函数跟前面链表的销毁是一模一样的,也是只穿透删除第一条线,没有写第二条线。同理,写多级指针时,必须要判断一下是否为空(有空则会崩溃),原创 2024-02-28 13:25:18 · 359 阅读 · 0 评论 -
双向链表(1)
DList指向整个表,DNode指向头结点,头结点后面串起了整个表,所以虽然两个的含义不同,但在计算机里面的用法是等价的,都是代表整个表。代码崩溃的主要原因之一就有——访问了空的指针,特别是在什么的什么的什么,这样的多级指针时,其中某一级指针为空时,整句代码都将崩溃。1.p的next置为500,p的NULL变为500——p->next = plist->next;p的next的prio中的100变为800——p->next->prio = p;(p不为空,则访问p的next或是访问p的prio都没错)原创 2024-02-26 19:42:04 · 353 阅读 · 0 评论 -
不定长顺序表4
所以,如果我们的程序是一运行完成就结束释放的,那么可以不调用销毁函数。前面初始化已经开了一个内存空间来放这个顺序表,那么这个顺序表使用完后不需要这个表了,就要把前面开出来的内存空间销毁了。malloc在实现里,free在调用里。像我们前面一堆的函数测试里面,我们发现程序运行输出完之后并没有内存泄漏崩溃,那是因为这里程序已经结束,进程已经退出了,那么系统就会自动释放刚刚运行的内存。那么不退出,就意味着不会释放内存,那此时有内存不停泄漏的话,就会不断消耗内存,内存越来越小,程序运行就越来越慢了。原创 2024-01-19 09:00:14 · 409 阅读 · 0 评论 -
循环链表4
malloc的原本用法就是malloc前面有一个void*的指针,因为malloc申请出来的结点需要一个指针去指向它,才能让这个新结点被找到,但这个指针没有返回值,没有名字,所以在后面sizeof里面写完你要定义的结点类型后,前面对应的就要强转类型了。所以只要有Node*(除了强转类型)就是定义指针,只是看它有没有接收结点,接收了结点的话后面写它就是代指其接收的结点,也就是p表示一个结点,p和p的next的实际值就是那一长串数字地址,将数字地址赋值也就是改变指针指向,而地址必须要存起来,否则链条就会断掉。原创 2024-02-26 05:22:34 · 843 阅读 · 0 评论 -
循环链表3
这个条件就很容易被遗忘写上,它一般写在循环体的最后一行,类似相当于循环执行一遍后的i++,然后在i++之后再判断循环条件2. 如果遗忘不写i++,就会造成死循环出不来等等问题。要改变链表结构,就要依赖前驱,每个前驱的next存储着下一个数据结点的地址,也就是依靠前驱的next来绑着后面一长串的数据结点,前驱就是从plist开始,结束于p->next的位置。所以用for循环的好处就是3个条件参数可以明确的写出来,不会遗漏。位置pos(在无特别说明的情况下)是从0开始计数的。原创 2023-11-21 18:31:51 · 65 阅读 · 0 评论 -
循环链表2
如果直接还是让p->next=plist,那就是直接让头插进去的数据元素跟头结点形成总共为2个结点的循环链表,丢掉了后面的一串数据,也就是后面的线没绑起来。例如当第二个有效数据元素进行头插时,先绑后面的,p->next被赋值为plist->next的值,而plist->next就是绑的后面一串数据。所以要用p->next=plist->next,它可以让第一个头插的结点绑向头结点,后面头插的结点绑住后面的元素线。——>左边像后面顺序连接。这里先绑后面的,后面是空就绑空,是数据1的结点就绑1结点。原创 2023-11-21 13:09:07 · 275 阅读 · 0 评论 -
循环链表1
如上图,有的会在初始化这里malloc一个plist,那么此时的头结点就不是临时结点了,在可扩容顺序表里面,清空函数不能销毁malloc出来的东西,需要free,所以不能调用。那么什么时候需要调用它——在清空就能达到销毁整个动态内存的的要求时,就可以调用它。再销毁时,有时候会调用清空函数,有时候又不调用清空函数;在单链表里面,清空数据调用销毁函数能够达到清空的要求,那么直接调用就行。在定长顺序表里面,就可以调用——是因为定长顺序表里面就没有动态内存。————一个malloc对应一个free。原创 2023-11-15 15:26:45 · 41 阅读 · 0 评论 -
单链表(8)
在不改变表的的结构的前提下的操作(打印,查找),用第二条p=plist->next(这是打印的第一个数据结点)获取前驱地址(目的为了删除,在插入和删除操作里面,找pos位置时,出循环后,p指向的都是pos位置的前驱)。用第二条(不改变链表原本的结构)的有:获取结点个数,查找key值search,输出show。综上所述:在进行要修改表的结构或是依赖于前驱的操作(插入,删除)时,用第一条p=plist。用第一条(要改变链表的结构,增加,减少结点个数等)的有:尾插,插入,删除pos位置值,原创 2023-11-13 14:48:48 · 53 阅读 · 0 评论 -
单链表(7)
如果结构还要留下继续下一次的使用,那么在下一次往后的每一次的插入删除操作进行之前,还要再多查一次这个结构还剩下几个结点,人也记不住,剩下旧的结点跟新的操作需求的结点一般也对不上,用完后还要再执行申请新的结点,多余些复杂不必要的步骤(每次操作前都要判断),——所以直接连结构一起销毁更好。这就是链表的插入比顺序表快很多的特点,顺序表中要插入一个数据,就把它后面的所有数据挨个往后移动一个格子,一个插入所有元素就要全移动一遍(特别是如果要头插,插入0号位置),所以说顺序表就是——大量的移动格子,也让速度慢了很多。原创 2023-11-13 14:26:58 · 53 阅读 · 0 评论 -
单链表(6)
也就是先知道前驱地址,然后让前驱的next指向要被删除的val数据的next,所以删除就是——穿透着删除,将300改成700。其实Node*==List,也就是说在代码里将List换成Node*,或者把Node*换成List,代码是没有任何问题的。现在数据2的next变成了700,此时地址300的结点就不知道在哪儿了,找不到300在哪儿就已经造成内存泄漏了,获取key值的后继地址函数——返回key的后继地址,如果不存在(key无后继,在表尾)返回NULL。所以要在删除它之前,把它标记一下,用q。原创 2023-11-12 23:20:03 · 53 阅读 · 0 评论 -
单链表(5)
还有这样写的,出来的结果也是一样的,它也算是对的——但是,这是前面多算一个头结点,后面少算一个结点数据,加1减1刚好答案一样而已。查找key值函数——在链表plist中 查找第一个key值,找到返回key值的结点地址,没有找到返回空NULL。*指针有指向符指向某个内容的时候,就一定要判断一下指针是否为空,如果指针为空但没有判断的话,就会崩溃。如图,p->next=NULL就跳出的话,当前p->data就没算上。例如要找数据3,找到了就返回3的地址300。这个求出来的是这几个方框内的数字。原创 2023-11-10 13:36:23 · 59 阅读 · 0 评论 -
单链表(4)
但前面说了移动plist不好,plist最好一直不要动。虽然这里移动plist也可以实现且不影响外面的实际数据,因为这是函数内部的头结点,其只在函数内部移动。函数外面的真正的数据的头结点并没有移动改变。还有一种尾插方法是将尾插函数内部的头结点plist移动来寻找尾巴,找到尾巴后插入新结点p。比如说指针p找到尾巴了,现在将指针p指向新的结点,尾插就好了。尾插函数跟头插函数唯一的不同就是找尾巴。这里的p类似于头插函数中的plist。可以看到头插是逆序的,尾插是顺序的。这里的头结点是不动的!原创 2023-11-10 13:35:28 · 51 阅读 · 0 评论 -
单链表(3)
而show输出的数据是从plist->next的数据开始输出的,前面说了plist的数据域(plist->data)无效不用的,所以是plist->next->data为第一个。这里要用到动态内存的申请malloc,因为如果不是动态申请的,那在当前函数结束后,里面的就都释放没有了(这个函数执行完到下个函数,对于申请的东西还没用上没对其进行操作呢,申请的东西就没有了,申请了个寂寞),而动态内存则需要free才能释放。然后不停的点逐语句,让p->data打印的从19逐渐变到3,2,1,然后慢下来。原创 2023-11-08 17:40:13 · 54 阅读 · 0 评论 -
单链表(2)
可以将顺序表的头文件复制到单链表里面,将里面所有的DPSQList替换为List,所有的PS替换为plist,所有顺序表替换为链表。也就是DPSQList PS换为List plist。顺序表PS换为链表plist。在链表里面的结点的地址 作用相当于顺序表中的下标,所以其查找的返回类型为Node*。在查找key值前面加一个,获取数据结点的个数,也就是获取有效数据的个数。单链表比较于顺序表,多了个插入中的,在插入函数前的头插和尾插。返回前驱回后继的数据的地址也同理。设计好的结构写在头文件里面。原创 2023-11-07 14:48:11 · 51 阅读 · 0 评论 -
单链表(1)
((*)为指针,其前面写的为指针的类型,即指针指向的东西的数据类型是什么,后面写的为指针的名字,先给指针起个名字叫next,就是下一个地址的意思;而指针的类型还是跟它一样的struct Node,因为它指向单链表中的下一组跟它当前的结构样子是长的一样的,单链表中每一组都包含2个成员,数据和指针。同理删除是直接穿过要删除的数据指向下一个数据,也要改变指针方向。单链表的形式就成了,保存了地址为300,就指向地址为300的数据200,保存了地址为100,就指向地址为100的数据150,保存地址为空,就完了。原创 2023-11-07 14:01:19 · 57 阅读 · 0 评论 -
不定长顺序表3
总结一下:插入时从length-1的位置(下标)开始,包括length-1位置,逐个往后移动,直到空出pos位置,然后在pos位置插入。删除时从pos+1的位置开始,包括pos+1位置,逐个往前移动。跟查找一样,不管能否扩容,删除都是在有效数据内部删除,删除方法都是一样的,所以还是把定长顺序表的删除函数搬过来就行。(4)有i+1的值赋值给i时,或是有别的出现i+1的情况时,务必记得(2)防止i+1越界,i的条件判断要跟着改。——在顺序表PS中 查找第一个key值,找到返回key值的下标,没有找到返回-1。原创 2023-10-30 08:42:08 · 54 阅读 · 0 评论 -
不定长顺序表2
所以别人在使用这个顺序表的时候,不会出现使用着使用着就因为满了而无法使用的情况,所以别人在使用顺序表时不需要对顺序表判断是否满了的操作。你没有空间了,你要是不可扩容的,那我就怎么样,我就插不进去,你要是能扩容,那我就插成功了。我们平时在使用顺序表插入信息时,都是直接插入信息,没有说上来第一步先判断这个表满了没,我们使用者要做的就只有插入,判满扩容是人家底层自动就做了的。这里说一点,i++的下一步是判断不是执行,1,2,3,3放着,4执行1,2判断3,符号要求才进4,不符合2,3虽执行但不进4。原创 2023-10-30 00:02:03 · 86 阅读 · 0 评论 -
不定长(可扩容)顺序表
然后我往图里的格子插入数据,不断length++,将数字9插入最后一个格子时,此时length的值就等于listsize的值,也就是说,有效数据的值==总容量的值时,这时我们就判断满了。现阶段的结构体就变成了这样,上下2个格子,上面放指针(例如整型int)指向我们后面的动态内存地址(例如像上图刚开始指向一块10个格子大小的内存地址)。我们在前面定长为10的顺序表基础上,不定长就是如果前10个格子满了,就在它的表尾扩个容,例如再加10个格子,如果后面用完了就再接着加格子扩容。原创 2023-10-27 15:03:49 · 31 阅读 · 0 评论 -
数据结构-顺序表6
比如现在有一张课表,那我现在要把这个课表里面的东换成上从上学期的课表,我换成这学期的课表,那我要换课表,我这个课表结构还要呢,只是把原来所有的数据清空了,换上新的数据(新课表)。把所有数据都清了,就是说所有的数据我都不要了,但这个顺序表的结构我还要呢,只是表里面的有效数据都不要了,也就是有效数据为0。这里把i<=0写成i<0,也是可以的,因为i==0接着进入下面return i-1后跟上面的return -1一样都是返回-1.代码有多样性不是每个人都一成不变的,但各种条件方面我们都要考虑到。原创 2023-10-27 13:07:17 · 84 阅读 · 0 评论 -
数据结构-顺序表5
比如说现在写的这个text文件就是一个正常的用户在使用的过程,那我使用我去插入,我插入完,那要不要说插入完一条新的学生信息后我把所有的学生信息再打印一遍,我不需要,我只需要往里边插入就好,我就默认你肯定成功了。所以我写的那我只能自己测一下,要不然我怎么知道我写的对不对。1.将2号位置的9前移覆盖掉pos位置要删除的数据(被覆盖丢弃即被删除),此时2号位置为空,将3号位置的10前移到2号位置。2.删除val(即上面的删除pos位置的值的函数)。删除pos位置(1号位置)的数据5,我们要做的就是。原创 2023-10-27 11:09:01 · 64 阅读 · 0 评论 -
数据结构-顺序表4
在上述测试的5个方面的条件中,1,2均为“没有找到该值”,3,4,5均找到其位置。这些测试可以反应我们在函数的实现中“参数判断”或“移动边界条件判断”等方面是否存在问题。顺序表怎么样就可以判断为一个空表——表中的有效元素数据的个数即长度为0.说一下一个好的测试习惯要怎么测。我们这个数据量不太多,正常测的话可以从头到尾(用for循环)全测一遍。一般应该有5个方面:(在数据量很多的情况)在有效数据长度的内部的随机位置的数值。在有效数据的2个边界处(线的两端)在有效数据范围之外的无效数据。1.下标小于0,左边。原创 2023-10-26 15:32:07 · 42 阅读 · 0 评论 -
数据结构-顺序表3
这就跟我们现在写的(.h)和(.cpp)的多文件同理,stdio.h文件里放了printf输出函数的定义与声明,那就应该有一个类似于stdio.cpp的文件(不一定叫stdio.cpp这个名字)存放写这个printf函数的功能实现, 而我们这个(.cpp)文件里就是在调用,使用printf这个函数,所以只用引用其头文件,不用管其怎么具体实现的。(前面也说过PS是从外面传进来的,你也不知道每个使用者都是什么想法,如果就有人传一个空的进来,为了保证代码的算法的健壮性,就要进行参数判断)原创 2023-10-26 11:29:00 · 50 阅读 · 0 评论 -
数据结构-顺序表2
这里我们需要把成员length的值初始化为0,而elem里面可以不改,继续存放随机值,因为length已经为0,那就说明elem里面的随机值都是无效的数据,所以这时elem里面的值改不改都无所谓,不影响。那么比如我们这个数据结构里面有2个数据成员,那么这2个就都要考虑一下,是可以继续存放它里面的随机值,还是为了后面正确的使用,要把它变成一个什么我需要的特定值(非随机值)。我们首先来看 初始化 是干什么的,如果我们在定义好一个顺序表后不初始化,那么顺序表里面的值就都是随机值,一开始PS是指向elem的。原创 2023-10-25 15:49:43 · 38 阅读 · 0 评论 -
数据结构-顺序表
先来看一下对于(定长)顺序表(内部),有哪些常用的基本操作,不定长(长度不固定)的顺序表的操作不一定是这样。我们先写(.h)头文件,即先写顺序表操作的函数的定义与声明(函数原型)原创 2023-10-24 19:41:06 · 18 阅读 · 0 评论 -
多文件建立
但是这个B文件里面可能又存有一部分A文件的内容,你不知道你直接就引用#include“ A.h ”,#include“ B.h ”,#include “C.h”, 结果B里面又含有了部分A的内容,这不就重复引用了吗?那么就需要在text里面引用(#include)存放Max函数的定义与声明的头文件(.h),函数的使用者只关心函数的原型是什么样,要不然它不知道函数的返回值是什么,有几个参数,都是什么类型的。与(.h)的头文件同名的(.cpp)的源文件,来放头文件中的函数的实现。原创 2023-10-24 14:39:55 · 23 阅读 · 0 评论