
数据结构
筱羊冰冰
这个作者很懒,什么都没留下…
展开
-
二维数组用三元组压缩时的转置和基本运算(加减乘)
三元组压缩在之前的博客《数组的压缩存储》中提到过,对于一个稀疏矩阵我们可以采用三元组的方式来进行压缩,即存储行、列、数值信息这三元来代替一个二维数组。之前博客的截图:转置将一个二维数组的每一个数据的行列信息对调得到的新矩阵就是原矩阵的转置。如矩阵:0 0 0 11 0 0 20 0 1 2变成:0 1 00 0 00 0 11 2 2的过程就是求转置的过程。转置矩阵的行列数和之前的列行数相同,则有可能后来的数组格式和之前不同,所以需要采用新原创 2020-08-15 01:05:03 · 1523 阅读 · 0 评论 -
字符串的模式匹配问题(朴素算法和kmp算法)
串说白了就是处理char数组的问题。有一个子串,就是一个串的一部分。涉及串的操作也不多,插入删除,查找子串。存储结构的话,其实用链表也行,但是会很繁琐模式匹配什么叫模式匹配呢,就是在一个串中匹配给定子串的起点下标,匹配不成功返回-1。回溯法看名字就能猜到含义,就是匹配的过程中如果和子串出现不同,就要在将子串回溯到起点,主串回溯到第一个匹配成功的点的下一个。举个例子abcdefgbce第二个匹配成功了,直到主串的d和子串的e匹配失败了,此时主串要回溯到第一个匹配成功点的下一个,就是c,原创 2020-08-13 11:29:54 · 548 阅读 · 0 评论 -
排序——希尔排序(来自曾经被其伤害过的人)
简介希尔排序,又称缩小增量法。过程:先取定一个整数d1<n,把全部结点分成d1个组,所有距离为d1倍数的记录放在一组中,在各组内进行直接插入排序;然后取d2<d1,重复上述分组和排序工作,直至取di=1,即所有记录放在一个组中排序为止。嗯,好了,上面的看一下就得了,不然就和我一样了,当我看明白了这行字的时候,感觉自己被学校ppt演的死去活来。。其实就是选择一串数组,比如531,只要第一个小于数组长度且最后一个为1就行了。然后要做的,就是按照数字分组:a[0] a[5] a[10]原创 2020-08-05 22:35:56 · 368 阅读 · 0 评论 -
基本排序算法——冒泡(单向&双向冒泡)、插入、选择、奇偶
接下来的坑是排序算法,首先给出几个以后都用得上的名词:时间复杂度:一个程序对n个数据需要的时间,考虑为n的函数空间复杂度:定义同上,不过是对空间上述两个一般只取n的函数的最高阶无穷小并且忽略倍数,如2n3+n2+5取n3即可;稳定/不稳定:是否交换了不需要交换的序列,检验方式为观看两个相等的数据(a[i]=a[j],其中i<j)在排序后相对顺序不变的为稳定比如121先交换两个1,然后交换成112(纯属口嗨,应该是没怎么皮的排序方式,但不稳定的算法还是有的)基本算法冒泡排序又叫气泡原创 2020-08-05 21:29:36 · 498 阅读 · 1 评论 -
二叉树的一些非递归问题——求宽度、任意结点的层数、祖先节点、完全二叉树判断
求宽度宽度,就是将二叉树按到根节点的距离(层数)排好,一层中最多的节点个数叫做宽度比如这个二叉树,宽度是4,在第三层达到最大。思想:利用队列和按行遍历的思想,同时记录下每一个结点的层数,最后数层数就好了。怎么知道层数?将根节点定义为第一层,每一次压入孩子就加一层喽。代码:typedef struct Btree//树的结构体{ char data; struct Btree* lchild; struct Btree* rchild;}btree;struct原创 2020-08-05 14:00:52 · 840 阅读 · 0 评论 -
二叉树的应用——二叉排序树、选择树、判定树
堆原创 2020-08-05 10:54:28 · 6149 阅读 · 1 评论 -
二叉树的应用——哈夫曼树
什么是哈夫曼树很简答啊,是哈夫曼提出来的(雾)先举一个生活中的例子:我们都知道考试成绩这东西正常接近于正态分布,那我假设一下90+占10%80-90占15%70-80占25%60-71占45%60及以下占5%然后有一个成绩读入的系统,要统计每一个分数段都有谁,我们可能需要一个switch语句,或者暴力一点if&else,如果我把90+作为比较第一项,那么很多人第一次都不会通过,然后是第二次判断。。把不及格的放在前面也是这个道理。都知道如果比较的次数多了,时间肯定要长,那么有没原创 2020-08-05 10:01:34 · 1712 阅读 · 0 评论 -
一般树和森林的存储和遍历
存储先讨论一般树双亲表示法(数组结构存储)孩子表示法(邻接表结构存储)孩子兄弟表示法(转换成二叉树存储)在一一介绍之前,想要说的是不论哪一种方法,我们都要先知道要干什么,然后选择方法。双亲表示法结构体数组,按照按行遍历的结果放在数组中,并且记录每一个结点的双亲结点是哪个(除了根节点),还是数组第一位不存储信息T[3].data=‘C’T[3].parent=T[1].data=A这样的结构有利于寻找双亲结点,如果是一个需要回溯的过程,双亲表示法是一个不错的选择孩子表示法说有这么原创 2020-08-03 22:12:28 · 412 阅读 · 0 评论 -
让二叉树更好——三叉树和线索二叉树(遍历的简化)
你是否已经厌倦了,那只能找到孩子却不能反向寻找双亲结点,那遍历起来想要砸键盘的、陈旧的二叉树?今天,就是我们站起来了的时候了。(雾)好了不皮了,先看一下定义三叉树这个名字不是指度最大为三的树,而是在二叉树(两个指针)的基础上多了个指针,因为二叉树找双亲结点只能用栈存储然后回溯,或者只能一个个结点的寻找,非常麻烦的。(别问名字为什么这样,问就是学校ppt教的,不过也很形象)好处:首先就是找到从根节点到任意节点的路径方便了很多;另外是在非递归遍历的时候,之前的二叉树结构想要回溯就只能借助栈,而现在原创 2020-08-03 19:13:13 · 2217 阅读 · 0 评论 -
二叉树递归求各种度数的结点数
度数这里的度数指出度,毕竟二叉树考虑入度就直接遍历数数么。因为是二叉树,所以度分为0,1,2三种。(有一说一直接遍历数点也行,不过今天想利用递归性质处理)叶子int leaves(btree* bt){ if(bt) { if(!bt->lchild && !bt->rchild) return 1; else return leaves(bt->lchild)+le原创 2020-08-03 15:06:11 · 970 阅读 · 0 评论 -
利用已知的遍历顺序回溯二叉树
前言之前有一个博客讲的是如何将一个中缀表达式改成后序表达式,然后代码就翻车了,数据反复横跳,后来想起来一个问题,单独一个中序遍历的结果是不能回溯出唯一一个二叉树,所以后序的结果自然不唯一。有人可能要问了,二叉树和我中缀表达式有什么关系(你抓周树人和我鲁迅有什么关系)那你看一下这个图:中序:H * I- E + F / G这不就是一个中缀表达式么,然后后序遍历:H I * E - F G / +就是中缀表达式的一个后缀表达式这个图说明了一个表达式可以使用二叉树来存储,叶子为参数,非叶子结点为原创 2020-08-02 15:02:38 · 610 阅读 · 0 评论 -
二叉树的三种遍历(先序中序后序)——递归非递归算法
回忆在上一个关于树的博客提到了二叉树的三种遍历方式,还有一个单独的层次遍历。先序、中序、后序本质山就是根、左、右的顺序问题先序:根左右中序:左根右后序:左右根递归算法因为二叉树的定义(其实应该说树的定义)里面有递归的影子:每一个子树也要符合上述条件(具体参见上一篇博客)所以递归算法应该是最先想到的,而且因为递归的性质,函数形式也是最简单的。先序:在这里插入代码片中序:...原创 2020-08-02 10:41:01 · 1168 阅读 · 0 评论 -
二叉树的一些递归小问题——求深度、复制、等价、相似、格式输出、判满二叉树、存储格式转换(二叉链表->数组)
求深度深度的定义已经在之前给出了,不做赘述int depth(btree *bt)//求深度{ int ldepth,rdepth; if(!bt) return 0; else { ldepth=depth(bt->lchild); rdepth=depth(bt->rchild); if(ldepth>rdepth) return ldepth+1;//加一是根原创 2020-08-02 00:57:25 · 243 阅读 · 0 评论 -
二叉树的层次遍历
层次遍历说白了就是将一个二叉树的结点按照层数放好,然后从第一层到最后一层,从左到右输出。从4到5这个过程就是直接跨越了子树,相当于是从一棵二叉树到另一个,很明显和三种遍历一样需要先将结点存储才能继续。但是,还能用栈存储吗?将2存储了走到3,如果接下来处理的是2的左子树,如果是栈应当弹出结点3,很明显不符合我们的需求。所以,这次使用队列。准备工作struct Queue//队列{ btree* data; struct Queue* next;};struct infor原创 2020-08-02 00:19:16 · 337 阅读 · 0 评论 -
二叉树的存储方式——数组&指针
二叉树的数组存储有一说一,看到这个很多人应该是诧异的,因为就二叉树那个尿性 ,一对多的性质,如果是使用数组存储,线性表和一个二维性质的图形怎么能对应上?这就要提到上一篇树的博客的一个性质(说过在存储会用上的)因为每一个结点(除根节点)都有双亲结点,也可能有孩子结点,他们的位置在图片上都有显示,只要按照这个规则来,就可以实现了。(a[0]不存储)a[1]:根节点a[2]:根的左孩子a[3]:根的右孩子以此类推另外不要忘了按行遍历,因为这样数组顺序和按行遍历的顺序刚好是相同的(对于完全二叉树)原创 2020-08-01 10:43:46 · 906 阅读 · 0 评论 -
树的结构——基本概念和二叉树的五条性质
下面开始数据结构的树部分了。树的定义首先要有一个根节点,然后从左到右有若干个子树,子树之间不可以相交,且每一个子树都符合上述要求的(子树可以为空)叫做树。在映射上树属于一对多的形式,之前的线性表可以理解为一对一的关系。注意到,每一个子树也符合上述定义,说明树有一个递归的性质,后面会举例一些关于递归性质的例题术语一般将根节点称为第一层,将距离根节点距离相同的结点放在一起构成一层,最大的层数叫做树高,(图中树高为5)每一层结点个数的最大值为树宽。 (图中树宽为2)原创 2020-07-24 15:28:47 · 595 阅读 · 0 评论 -
数组的压缩存储(特殊矩阵&稀疏矩阵)
几种特殊数组其实这里就不应该说是数组了,而应该是说行列式,对称矩阵:a[i][j]=a[j][i]上下三角行列式:对角线以下/以上的元素均为0带状行列式:这个可能不太好理解,举一个例子1 1 0 0 01 1 1 0 00 1 1 1 00 0 1 1 10 0 0 1 1这时看对角线比较舒服,可以看出来有三条斜着的线上的元素是aij(例子里面均是1),而剩下的是0.介绍一个名词,带宽,就是带状行列式的宽度,为第一行非零元格个数减一,记为s。原创 2020-07-22 11:09:57 · 803 阅读 · 0 评论 -
栈和队列的应用——火车进站的排序问题
是学校实验课的内容,本人已上网验证过,已经有很多人发过了。1.火车乱序输入输出名字不太好起,内容是这样的:输入:4//火车数1 2 3 4//火车进站的顺序4 1 2 3//希望的火车出栈的顺序如果能实现,输出可以;不能,提示不能按照上面的顺序输出。火车可以在岔道口等着,但根据生活常理,后进的火车必须先走了,先进的火车才能走。(这和啥一样?)我这里采用的方式是记录输出的火车数目,退出循环的条件是输出数目和输入的火车数相同。用两个下标遍历进站出栈的顺序,如果是同一个火车,就直接出栈就行了原创 2020-07-21 20:03:39 · 2759 阅读 · 0 评论 -
队列的线性实现——循环队列
从上一篇博客说起之前说过,如果是单纯一个数组就很容易造成假溢出,那么就有人会想到,如果我把一个数组看成是一个环,将最后一个元素和第一个元素连上(实际的存储结构还是数组,这个不可能连上),那么是不是就可以解决假溢出了?实现为了出对入队方便,设两个下标(int型)分别指向队头front(第一个元素)和队尾rear(最后一个元素的下一位)。那么此时有计算公式:插入:队头不变,rear = (rear+1)%length删除:队尾不变,front = (front+1)%length但是,在队列为空的原创 2020-07-21 10:49:19 · 177 阅读 · 0 评论 -
线性表的应用2——快慢指针问题
快慢指针貌似是哪个大厂的面试题吧,让你找到一个链表的中间节点,第一反应遍历数个数,然后跑到中间那个就行了呗。貌似标准答案也差不多这样,但是有一个dl就不一样,他的思路是这样的:两个指针,一个慢,一次走一格;一个快,一次走两格。当快指针到最后,慢指针就到了中点位置了。例一,求链表倒数第k个结点的元素利用上面的思想,快慢指针,不过这次是有一个指针先走k’格,然后两个一起前进,当先行的指针到最后,另一个指针的位置就是倒数第k个。例二,存储两个后缀相同的英文单词说有两个英文单词,每一个字母都是由链原创 2020-07-19 21:53:47 · 151 阅读 · 0 评论 -
栈的应用3——迷宫求解问题
为什么用栈看标题就是老计算机问题了,因为计算机的性质,穷举法是一个很好的方式,那么就需要记录当前点到出口的路径,这时,栈就派上用场了。规定1.创建一个二维数组maze,0表示可以通过,1表示不行;2.因为有八个方向可以走(别问为什么是八个,开始听我也感觉很扯,后来就习惯了),因为每一个方向都有一个坐标的变化,用二维数组存储避免了每一次都要进行计算。move数组:(到时候只需要i+move[方向][0]就行了)3.防止每一次都测试边界,在当前的二维数组加一圈1;4.另辟一个数组mark,专门原创 2020-07-19 09:13:10 · 294 阅读 · 0 评论 -
特殊的线性表--栈和队列介绍
栈一种操作受限制的线性表,遵从LIFO原则。是不是听起来很高端呢,其实就是 last in last out (后进后出)想想小时候的玩具手枪弹夹,就是这个道理。栈的结构就是线性表,两端分别叫做栈顶和栈底,插入和删除只允许在栈顶进行。存储结构1.数组:比较好处理,以数组的最后为栈顶进行操作(如果是a[1]的话插入删除绝对要人命)a[0]一般是不用,刚好来存储最后一位的下标。2.链表:相对于表尾,一个带表头的链表的前端更适合作为栈顶,免去了找最后一个的麻烦。有时候也会额外整一个整型变量记原创 2020-07-18 20:27:31 · 837 阅读 · 0 评论 -
栈的应用4——递归函数的非递归形式(主讲阿克曼函数的非递归形式)
递归函数自己调用自己如求阶乘函数:int jiecheng(int n){ if(n==0) return 1; if(n==1) return 1; else return n*jiecheng(n-1);}脑补一下如果在主函数里面调用这个函数输入5的话,函数返回5*jiecheng(4),所以这个函数还要继续调用下去,这就是自己调用自己的函数——递归函数。举例说明:汉诺塔、阿克曼函数、阶乘、斐波那契数列和栈的联系递归函数最后要返回 5!,但是他最开始只能知道答案是原创 2020-07-18 19:50:40 · 594 阅读 · 0 评论 -
栈的应用2——中缀表达式和后缀表达式的转换
中缀后缀日常生活中,我们都是使用中缀表达式,如8*(5+3)这样的,这样的式子可以画成一个二叉树,而二叉树的遍历方式有三种,分别对应着三种表达式方式。(感觉说多了,再说后面二叉树没东西讲了)所以上面的式子可以写成8 5 3+ * ,虽然看着很诡异,但是要知道,对于计算机来说,看中缀表达式比看十进制难受多了(栈的应用1——数制转换),因为:后缀表达式可以避免括号游戏规则在这道题里面,可以输入大小写的单字母变量,以及双目四则运算(加减乘除)还有括号,目的是将这个转换成一个后缀表达式,以去掉括号。先原创 2020-07-17 14:39:21 · 326 阅读 · 0 评论 -
栈的应用1——数制转换
数制一般分为二、八、十、十六这几种常见的(没错就是我现在能见到的)x进制就是0~x-1构成每一位,就如二进制是0、1,八进制是0到7数制转换数制转换一直是计算机的一个基本问题,目前已经有了很多的解决方法。如果是其他进制转换十进制(因为十进制多数人还是比较熟悉的,毕竟买菜时价钱不是二进制的)先找到每一个数字的位数:整数部分将个位置为0位,往前依次加一位;小数部分第一位是-1,往后依次减一;因为是二进制,所以应该是二的多少倍乘以原来二进制的数字,如个位应该是:2的零次幂乘一。求和就可以得原创 2020-07-16 14:34:30 · 598 阅读 · 2 评论 -
线性表练习1(两个链表的合并 链表的原地反向 数组的循环移位)
两个链表合并到一个新的链表这种的要求不高,就是原创 2020-07-12 20:05:57 · 203 阅读 · 0 评论 -
线性表的应用1
应用1 一元多项式a0+a1 * x1+a2 * x2+……这样的式子就是一元多项式了,只有一个变量且为指数项相加的形式。理论上用一个结构体数组就行了,但是呢,如果是a0 + a100 * x100这样的,要是一个数组,血亏。所以呢,链表就出来拯救世界了,此时结构体的数值域就应该是两个变量了,exp(指数)和...原创 2020-07-12 16:00:20 · 197 阅读 · 0 评论 -
静态链表(数组、链表和静态链表的操作)
因为是线性表,不能只讲链表,所以今天提一下静态链表以及数组、链表、静态链表之间的对比。数组基本结构没得说,插入和删除的操作也是有的(虽然看着不比较诡异)一般是动态分配一个足够长的,记录有多少个元素后对这个个数进行加减,而不是每插入一个就动态分配(其实这样也行,要是不怕麻烦的话)另外在插入删除操作之后要把这个点之后的元素都进行移动,还是相当麻烦的;不过数组有一个好处就是方便找到第几个元素。插入删除的时间复杂度问题和平均移动次数:默认了每一个结点的查找概率相同所以插入一个结点的概率是1/(n+1)原创 2020-07-10 15:22:32 · 925 阅读 · 0 评论 -
线性链表2(环形链表和双向链表,还有躲在角落瑟瑟发抖的双向环形链表)
环形链表按照上一篇博客的方式用head头结点的话,那构建环形链表的方式就是1.去掉头节点,2.将最后一个结点的指针域(原来是NULL)改成指向第一个结点这时作为函数头节点的就是原本最后的一个结点。(因为这样保证了头节点的下一个就是第一个结点)就长这样。和正常的单链表有所不同,首先是判空的条件,原来的为head->next 为NULL,而现在就是head为NULL其次在循环的时候就不应该以NULL为结束的标志了。最后,在插入和删除的时候,和之前稍有不同,建议画图体会一下。也可以不删除原创 2020-07-09 20:50:55 · 123 阅读 · 0 评论 -
线性链表1(基本结构、创建和遍历)
线性链表(准备分几块来考虑,这样我就能多水几篇文章了)基本线性链表环形链表双向链表双向环形链表(感觉这个就简单提一下吧)和一些链表比较有趣的习题静态链表首先给一下为什么要整链表这个东西,就是为了折磨人的,其实最开始只有数组的时候,因为数组的长度改变起来非常麻烦(其实想改变的话就是动态数组,具体我也没试过),然后dl就发明了链表。所谓链表,就是猴子捞月,一只猴子(数据域)用手(指向下一个链表的指针域)抓着下一个猴子的尾巴(指针),这样挂起来就是一个链表了。很明显,第一个猴子没有抓尾巴,也就是原创 2020-07-08 10:18:45 · 480 阅读 · 0 评论