《大话数据结构》读书笔记
2022-04-18 16:36:48 69 举报
AI智能生成
程杰的《大话数据结构》知识体系图
作者其他创作
大纲/内容
数据结构
逻辑结构(分为线性结构和非线性结构)
集合结构(非线性结构)
集合通常是由一组无序的, 不能重复的元素构成<br>数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系<br>
线性结构
线性结构是一个有序数据元素的集合
数组
线性表
存储结构
顺序存储结构
顺序存储结构,用一段地址连续的存储单元<br>一次存储线性表的数据元素。比如<b>数组</b>
优点:<br><ul><li>无需为表示表中元素之间的逻辑关系而增加额外的存储空间</li><li>可以快速的存取表中任意位置的元素,时间复杂度为O(1)</li></ul>
缺点:<br><ul><li>插入和删除操作需要移动大量元素</li><li>当线性表成都变化较大时,难以确定存储空间的容量</li><li>造成存储空间的"碎片"</li></ul>
链式存储结构
单链表
链表中每个结点可以包含若干个数据域和若干个指针域。<br>如果每个结点中只包含一个指针域,则称其为单链表。
优点:<br><ul><li>单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制</li><li>插入和删除更快,时间复杂度为O(n),在给出某位置的指针后,复杂度仅为O(1)</li></ul>
缺点:<br><ul><li>查找效率比顺序存储结构差,时间复杂度为O(n)</li></ul>
静态链表
用数组描述的链表叫静态链表(用数组代替指针)<br>
优点:<br>插入和删除只需要修改游标,无需移动元素<br>从而改进了在顺序存储结构中的插入,删除许移动大量元素的缺点<br>
缺点:<br>没有解决连续存储分配带来的表长难以确认的问题<br>失去了顺序存储结构随机存取的特性
循环链表
将单链表中终端结点的指针段由空指针改为指向头结点,<br>就使整个单链表形成一个环,这种头尾相接的单链表就<br>称为单循环链表,简称循环链表
双向链表
在单链表的每个结点中,再设置一个指向其前驱结点的指针域<br>(就意味着双向链表的结点都有俩个指针域,一个指向直接后继,<br>一个指向直接前驱)
栈(特殊的线性表)
限定仅在表尾进行插入和删除操作的线性表,又称后进先出(<b>LIFO</b>)的线性表<br>(允许插入和删除的一端称为栈顶,另一端称为栈底)
顺序存储结构(顺序栈)
链式存储结构(链栈)
队列(特殊的线性表)
只允许在一端进行插入操作,而另一端进行删除操作的线性表<br>先进先出(<b>FIFO</b>)
顺序存储结构(顺序队列)
循环队列
链式存储结构(链队列)
串(字符串)
有0个或多个字符组成的有限序列
存储结构
顺序存储结构
链式存储结构
模式匹配
朴素的描述匹配<br>(从头开始一个个字符匹配)
效率很低,复杂度为O((n-m+1)*m) <br>n是总的字符串,m是需要匹配的字符串
KMP模式匹配算法
KMP模式匹配算法改进
树形结构(非线性结构)
数据元素之间存在一对多的层次关系
二叉树
特点
<ul><li>每个结点最多有俩棵子树</li><li>左右子树是顺序的,次序不能颠倒</li><li>即使树中只有一棵子树,也要区分左右</li></ul>
五种形态
<ul><li>空二叉树</li><li>只有一个根结点</li><li>根结点只有左子树</li><li>根结点只有右子树</li><li>根结点既有左子树,又有右子树</li></ul>
性质
在二叉树的第i层上之多有2i-1(i-1是上标)个结点<br>
深度为K的二叉树至多有2k-1(k>=1,k是上表)个结点
...
特殊二叉树
斜树<br>
<ul><li>所有结点都只<font color="#000000">有左子</font>树的二叉树叫左斜树</li><li>所有结点都只有右子树的二叉树叫右斜树</li></ul>
满二叉树<br>
一个二叉树中,所有分支结点都存在左子树和右子树,<br>并且所有叶子都在同一层上,称为满二叉树
完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) <br>的结点数都达到最大个数,第 h 层所有的结点都连续集中<br>在最左边,这就是完全二叉树。
线索二叉树
每个二叉树左右俩个指针域,如果为空,放着也浪费空间。<br>所以第一次遍历二叉树时,如果左指针域为空就存前驱地址,<br>右指针域为空就存后继地址。但是左侧需要一个tag来标识左指<br>针域存的是左孩子结点还是前驱,右侧也同样需要。
存储结构
顺序存储结构
只用于完全二叉树(因为完全二叉树定义的严格,<br>可以用顺序结构表示出二叉树的结构,很有优越性。详情参考P172)<br>该结构不太通用,通用的见二叉链表
二叉链表
二叉树每个结点最多有俩个孩子,所以可以用<br>一个数据域+俩个指针域这样的结构存储二叉树
遍历
概念
从根结点出发,按照某种<b>次序依次</b>访问二叉树中<br>所有结点,使得每个结点被访问一次且仅被访问一次
遍历方法(限定了从左到右的习惯)
前序遍历
根结点=>左子树=>右子树<br>(切记:是先把所有左子树遍历完了,再遍历右子树)<br>
中序遍历
从根结点开始(并不是先访问根结点),遍历根结点的<br>左子树=>根结点=>右子树
后序遍历
从根结点开始(并不是先访问根结点),遍历根结点的<br>左子树=>右子树=>根结点
层序遍历(BFS常用)
根结点一层层从上而下遍历
哈夫曼树(也叫最优二叉树)<br>
带权路径长度WPL最小的二叉树称作哈夫曼树
哈夫曼编码
<b>哈夫曼编码实现压缩,解压缩</b>
原理:先构造哈夫曼树,然后对字符串再次编码,二进制的长度会明显缩小,<br>所以达到了压缩的效果。解压缩也要用哈夫曼树,才能知道哪几个二进制对应哪个字符
图形结构(非线性结构)
数据元素是多对多的关系
概念:有顶点的有穷非空集合和顶点之间边的集合组成<br>表示为G(V,E),其中G表示一个图,V是图G中顶点的集合,E是边的集合
特点:图中数据元素称为顶点,图中不允许没有顶点
术语
顶点
图中数据元素称为顶点,图中不允许没有顶点
无向边
顶点Vi到Vj(i和j是下标)之间的边没有方向,<br>则称这条边为无向边,用无序偶对(ViVj)来表示
有向边
顶点Vi到Vj之间的边有方向,<br>则称这条边为有向边,也称为弧
无向图
图中任意俩个顶点之间的边都是无向边
连通图
概念:从顶点V导顶点V'有路径,称V和V'是连通的。<br><b>如果对于图中任意俩个顶点都是连通的则称为连通图</b><br><b>(能连通就行,不一定要相邻)</b><br>
连通分量
<ul><li>要是子图</li></ul>
<ul><li>子图要是连通的</li></ul>
<ul><li>连通子图还有极大顶点数</li></ul>
<ul><li>具有极大顶点数的连通子图包含依附于这些顶点的所有边</li></ul>
生成树
一个连通图的生成树是一个<b>极小</b>的连通子图,<br>它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边(p221)
有向图
图中任意俩个顶点之间的边都是有向边
强连通图
概念:如果对于图中任意俩个顶点都是连通的则称为连通图<br>(能连通就行,不一定要相邻)
强连通分量
参考连通分量定义
有向树
概念:一个有向图恰有一个顶点的入口为0(根节点),<br>其余顶点的入度均为1,则是一颗有向树
简单图
不存在顶点到其自身的边(自己到自己),且同一条边不重复出现
无向完全图
在无向图中,任意俩个顶点之间都存在边
有向完全图
在有向图中,任意俩个顶点之间都存在方向互为相反的俩条弧
网
有的图的边或弧具有与它相关的数字,这叫做权。权可以表示一个顶点到另<br>一个顶点的距离或者耗费,这种带权的图称为网
存储结构
邻接矩阵
用俩个数组来表示图,一个一维数组存储图中顶点信息,<br>一个二维数组(邻接矩阵)存储图中的边或弧的信息
缺点
对于边数相对于顶点较少(顶点多,边数少)的图,<br>这种结构存在存储空间的浪费
邻接表
数组和链表相结合的存储方法称为邻接表
特点
<ul><li>图中顶点用一个一维数组存储</li></ul>
<ul><li>每个顶点的所有邻接点构成一个新线性表,</li></ul>
<ul><li>如果是有向图,还可以建立有向图的逆邻接表,</li></ul>即对每个顶点vi都建立一个链接为vi为弧头的表。<br>(可以很容易得到顶点的入度或以顶点为弧头的弧)
优缺点
想了解入度就必须遍历整个图才知道,反之,<br>逆邻接链表解决了入度却不了解出度的情况。俩者不可兼得
十字链表
把邻接表和逆邻接表结合起来
邻接多重表
主要优化无向图
边集数组
俩个一维数组构成,一个存储顶点的信息,一个存储边的信息。<br>这个边数组每个数据元素由一条边的起点下标,终点下标和权重组成
遍历
深度优先(DFS)
约定原则:在没有碰到重复顶点的情况下,始终向右手边走。<br>走回到顶点之后,还要按原路一步步返回,在一步步验证是否都都走过了
广度优先(BFS)
图需要变形下,改造成类似树那样有明显的层级关系的样式
最小生成树
概念:我们把构造连通网的最小代价生成树称为最小生成树
所谓最小代价,就是n个顶点,用n-1条边<br>把一个连通图连接起来,并且使得权值的和最小
算法
普里姆(Prim)算法
以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树
克鲁斯卡尔(Kruskal)算法
以最小权值边开始构建
最短路径
迪杰斯特拉(Dijkstra)算法
一步步求出与顶点的最短路径,过程中都是基于已经求出的最大路径的基础上
佛洛伊德(Floyd)算法
拓扑排序算法
物理结构(存储结构)
顺序存储结构
数据元素存放在地址连续的存储单元里,<br>数据间的逻辑关系和物理关系是一致的<br>(不方便插入,删除)
链式存储结构
数据元素存放在任意的存储单元里,这组存储单元可以是连续的,<br>也可以是不连续的<br>(很灵活,给个指针就能找到。需要一个指针存放数据元素的地址)
索引存储结构
为了方便查找,整体无序,但索引块之间有序,需要额外空间,存储索引表。<br>优点:对顺序查找的一种改进,查找效率高<br>缺点:需额外空间存储索引
散列存储结构
选取某个函数,数据元素根据函数计算存储位置可能存在多个数据元素存储在同一位置,引起地址冲<br>优点:查找基于数据本身即可找到,查找效率高,存取效率高。<br>缺点:存取随机,不便于顺序查找。<br><br>
数据运算
查找运算
顺序表查找
顺序查找(线性查找)
从表中第一个(或最后一个)记录开始,逐个进行记录的的关键字和给定值比较。
有序表查找
折半查找(二分查找)
切记:表必须是有序的。mid=(low+high)/2。复杂度是0(logn)
可以优化的地方:每次都从1/2处查找,对于有些场景还是不太适合的,<br>比如查找am单词,很显然从字典的前面查找效率更高,所以优化点是从哪里开始折半
插值查找
公式:mid=low+(key-a[low])*(high-low)/a[high]-a[low] ;low和high是数组的下标
适合场景:
表长较大,而关键字分配又比较均匀的查找表,性能比折半查找要好得多。复杂度也是0(logn)
斐波那契查找
构造另外一个斐波那契数列用于辅助
公式:mid=low+F[K-1]-1 ;F是斐波那契数列
比较:平均效率优于折半查找,最坏情况下,效率低于折半
线性索引查找
稠密索引
索引项一定要按照关键码有序的排列
分块索引
把数据集的记录分成了若干块,满足俩个条件
<ul><li>块内无序</li></ul>
<ul><li>块间有序</li></ul>
倒排索引(P312)
概念:记录号表存储具有相同次关键字的所有记录的记录号<br>(可以指向记录的指针或者该记录的主关键字)
<b>搜索引擎最基础的搜索技术</b>
<b>二叉搜索树(又称二叉查找树/排序数)</b>
特点:
<ul><li>若左子树不为空,则左子树上所有结点值均小于根结点的值</li></ul>
<ul><li>若右子树不为空,则右子树上所有结点的值均大于它的根结点的值</li></ul>
<ul><li>它的左右子树也分别为二叉排序树</li></ul>
优点:
是一个既使得插入和删除效率不错,<br>又可以比较高效率的实现查找的算法
缺点:
极端情况下,会退化成链表。时间复杂度为O(n)
<b>平衡二叉搜索树(AVL树) </b>P(329)
概念:<b>是一种二叉排序树</b>,其中每一个节点<br>的左子树和右子树的<b>高度</b>差绝对值不超过1
优缺点:
维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,<br>更多的地方是用追求局部而不是非常严格整体平衡的红黑树。<br>当然,如果应用场景中对插入删除不频繁,只是对查找要求较高,那么AVL还是较优于红黑树。
多路查找树(B树)
概念:每一个结点的孩子树可以多余俩个,且每一个结点处可以存储多个元素
4种形式
2-3树
每一个结点都具有俩个孩子(称为2结点),或三个孩子(称为3结点)
2-3-4树
2-3树的扩展,最多四个孩子
<b>B树</b>
一种平衡的多路查找树
所有叶子结点都位于同一层次
<b>B+树</b>
应文件系统那个所需而出的一种B树的变形树
出现在分支节点中的元素会在叶子结点中再次列出,每一个叶子结点都会保存一个指向后一叶子结点的指针
红黑树
概念:一种自平衡二叉查找树,和AVL树类似(并不是真正的平衡二叉树),<br>都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
适合:适合搜索,插入,删除较多的情况
性质:
每个结点要么是红色,要么是黑色
根结点永远是黑色
所有的叶节点都是空结点(即null),并且是黑色的
每个红色结点的俩个子结点都是黑色(从每个叶子到跟的路径上不会有俩个连续的红色结点)
从任一结点到其子树中每个叶子结点的路径都包含相同数量的黑色结点
关键性质:
这些性质强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。主要原因在于,<b>性质4导致了路径不能有两个毗连的红色节点,最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点</b>,根据性质5所有<b>最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长</b>
散列表查找(哈希表)
概念:散列技术是在记录的存储位置和它的关键字之间建立一个稳定的对于关系f,<br>使得每个关键字key对于一个存储位置f(key),f称为散列函数,又称为哈希(Hash)函数
特点
<ul><li><b>在存储时,通过散列函数计算记录的散列地址,并按此散列地址存储该记录</b></li></ul>
<ul><li><b>查找记录时,我们通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录</b></li></ul>
缺点
有时候俩个关键字不同,但是算出来的地址相同,称为冲突
散列函数构造方法
直接定址法
数字分析法
平方取中法
折叠法
除留余数法
随机数法
散列冲突的解决方法
开放定址法
一个冲突,就去寻找下一空的散列函数
再散列函数法
准备多个散列函数,有冲突就换一个
链地址法
公共溢出区法
冲突的都单独存储到溢出表中
排序运算
概念
内排序与外排序
内排序是在排序整个过程中,待排序的所有记录都放置在内存中。外排序是由于排序的记录太多,<br>不能同时放置在内存,整个排序过程需要在内外存之间多次交换数据才能进行
排序种类
冒泡排序
复杂度O(n2) 2是上标
正宗的冒泡排序是相邻元素俩俩比较
冒泡排序优化
加个flag,如果后面后面已经是有序的了,就不需要再继续后面的循环判断了
简单选择排序
每个元素依次和后面所有的元素比较大小
直接插入排序
将一个记录插入到已经排好序的有序表中,从而得到一个新的记录数+1的有序表
希尔排序
把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;虽则增量逐渐减少,<br>每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组
堆排序
利用堆这种数据结构所设计的一种排序算法
归并排序
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序
快速排序 P471
通过一次排序将待排序记录分割称独立的俩部分,其中一部分记录的关键字均比另一部分小,<br>则可分别堆这俩部分记录继续进行排序,以达到整个序列有序的目的
算法
时间复杂度(大O表示法)
推导方法
1. 用常数1取代运行时间汇总的所有加法常数
2. 在修改后的运行次数函数中,只保留最高阶
3. 如果最高阶项存在且不是1,则去除与这个项相乘的常数,<br>得到的结果就是大O阶
常见的复杂度
常数阶 => O(1)
线性阶 => O(n)
对数阶 => O(log2n,缩写为logn)
平方阶 => O(n2)<br>
0 条评论
下一页