线性表
栈与队列
串
树
- 度: 节点拥有的子树数。(叶子节点(终端节点、分支节点)度为0)。
- 树的度: 树内各节点的度的最大值。
- 树的深度(高度): 树中节点的最大层次。
- 树的路径长度就是从树根到每一结点的路径长度之和
- 树的带权路径长度为树中所有叶子结点的带权路径长度之和。通常记作 “WPL”
二叉树的性质
- 在二叉树的第i层上至多有2^(i-1)个结点(i≥1)。
- 深度为k的二叉树至多有2^k - 1个节点(k≥1)。
- 对于任意二叉树,如果叶子节点数为n0, 度为2的节点数为n2, 则n0 = n2 + 1。
- n个节点的二叉树一共有n-1条分支线数。
二叉树的遍历
- 前序遍历: 先访问根节点,然后前序遍历左子树,再前序遍历右子树。
- 中序遍历: 从根节点开始(注意不是先访问根节点),中序遍历左子树,然后访问根节点,最后中序遍历右子树。
- 后序遍历: 从左到右先叶子后节点的方式遍历访问左右子树,最后是访问根节点。
特殊二叉树
满二叉树
特点:
- 叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
- 非叶子节点的度一定是2.
- 同样深度的二叉树中,满二叉树的节点个数最多,叶子树最多。
完全二叉树
特点:
- 叶子节点只能出现在最下两层。
- 最下层的叶子一定集中在左部连续位置。
- 如果节点度为1,则该节点只有左孩子。
- 同样节点数的二叉树,完全二叉树的深度最小。
- 另外两条特点查看大话数据结构P170
- 满二叉树一定是完全二叉树,反之不一定成立。
线索二叉树 Threaded Binary Tree
添加了直接指向节点的前驱和后继的指针的二叉树成为线索二叉树。
使用场景
如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继。
- 有N个节点的二叉树中需要利用N + 1 个空指针添加线索。这是因为在N各节点的二叉树中,每个节点有2个指针,所以一共有2N个指针,除了根节点以外每一个节点都有一个指针从他的父节点指向他,所以一共使用了N-1个指针。所以剩下 2N-(N-1)个空指针。
- 对二叉树以某种次序遍历使其成为线索二叉树的过程称作线索化。
赫夫曼树(Huffman Tree)
给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。也称哈夫曼树、最优树、最优二叉树。
赫夫曼树构造
- 根据给定的n个权值构成棵二叉树的集合,其中每棵二叉树中只有一个带权为根结点,其左右子树均为空。
- 在F中选取两棵根结点的权值最小的树作为左右子树构造一棵新的二叉树,且置新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
- 在F中删除这两棵树,同时将新得到的二叉树加入F中。
- 重复2和3步骤,直到F只含一棵树为止。这棵树便是赫夫曼树
特点
- 权值越大的叶子节点越靠近根节点,权值越小的叶子节点越远离根节点。
- 只有度为0(叶子节点),和度为2(分支节点)的节点,没有度为1的节点。
应用场景:赫夫曼编码
图(Graph)
由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V, E), 其中G 表示一个图,V是图G中顶点的集合,E是图G中边的集合。
- 无向边: 若顶点vi到vj之间的边没有方向,则称这条边为无向边(Edge),用(vi, vj)表示。
- 有向边: 若从顶点vi到vj的边有方向,则称这条边为有向边,也称弧(Arc),用<vi, vj>表示,vi称为弧尾(Tail),vj称为弧头(Head).
- 无向图: 如果图中任意两个顶点之间的边都是无向边,则称该图为无向图(Undirected graphs)。
- 有向图: 如果图中任意两个顶点之间的边都是有向边,则称该图为有向图(Directed graphs)。
- 简单图: 在图中,若不存在顶点到自身的边,且同一条边不重复出现,则称这样的图为简单图。
- 无向完全图: 任意两个顶点之间都存在边。含有n个顶点的无向完全图有n(n-1)/2条边。
- 有向完全图: 任意两个顶点之间都存在方向互为相反的两条弧。含有n个顶点的有向完全图有n(n-1)条边。
- 连通图、强连通图、连通分量、强连通分量
1.存储结构
1.1 邻接矩阵-Adjacency Matrix
- 对于边数相对顶点较少的图,存在存储空间浪费问题
1.2 邻接表-Adjacency List
优化存储空间浪费问题
1.3 十字链表 Orthogonal List
- 把邻接表和逆邻接表结合起来的存储方式,容易找到以vi为尾的弧和以vi为头的弧,容易求得顶点的出度和入度。
- 除了结构复杂一点外,创建图算法的时间复杂度和邻接表相同。
- 有向图的应用中,十字链表是非常好的数据结构模型。
1.4 邻接多重表
优化无向图边的操作
2 图的遍历
- 深度优先
- 广度优先
3 最小生成树
- MST性质
- 普里姆(Prim)算法
- 克鲁斯卡尔(Kruskal)算法
4 最短路径
- 迪杰斯特拉(Dijkstra)算法 --单源最短路径
- 弗洛伊德(Floyd)算法–多源最短路径
5 拓扑排序
对有向无环图(Directed Acyclic Graph)构造拓扑序列的过程称为拓扑排序。
- 对于任何有向图而言,其拓扑排序为其所有结点的一个线性排序(对于同一个有向图而言可能存在多个这样的结点排序)。该排序满足这样的条件——对于图中的任意两个结点u和v,若存在一条有向边从u指向v,则在拓扑排序中u一定出现在v前面。
- AOV网:在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,称为AOV网(Activity On Vertex)
- AOE网:在一个表示工程的有向图中,用顶点表示活动开始或结束时间,用弧表示活动,这样的有向图为弧表示活动的网,称为AOE网(Activity On Edge)
- 对AOV网进行拓扑排序的基本思路:从AOV网选择一个入度为0的顶点输出,然后删除此顶点,并删除以此为尾的弧,继续重复此步骤,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止
- AOV网解决拓扑排序问题,AOE网解决关键路径问题。
- 解决有向图中的依赖解析问题,或者说解决一个工程能否顺序进行的问题。
6 关键路径
查找
1.1 查找表
- 静态查找表:只作查找操作的查找表
- 动态查找表:查找过程中同时插入查找表中不存在额数据元素,或者从查找表中删除已经存在的某个数据元素。
1.2 有序表查找
- 折半查找
mid = (low + high) / 2 - 插值查找
mid = low + (key - a[low]) (high - low) / (a[high] - a[low]) - 斐波那契查找
mid = low + F[k - 1] - 1
1.3 线性索引查找
- 索引就是把一个关键字与它对应的记录相关联的过程。
- 索引按结构可以分为:线性索引、树形索引、多级索引。
- 线性索引:将索引项集合组织为线性结构,也称索引表。
- 线性索引可分为:稠密索引、分块索引和倒排索引。
1.3.1 稠密索引
- 指在线性索引中,将数据集中的每个记录对应一个索引项。
- 对于稠密索引来说,索引项一定是按照关键码有序的排列。
- 由于索引项与数据集的记录个数相同,所以空间代价很大。
1.3.2 分块索引
为了减少索引项的个数,我们可以对数据集进行分块,使其分块有序,然后再对每一块建立一个索引项,从而减少索引项的个数。
- 块间有序,块内无序
1.3.3 倒排索引、反向索引
二叉排序树
- 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值。
- 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值。
- 它的左、右子树也分别为二叉排序树。
- 以链接的方式存储,保持了链接存储结构在执行插入或删除操作室不用移动元素的优点。
平衡二叉树、AVL树
注意平衡二叉树和完全二叉树区别。
- 首先它是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多为1
- 平衡因子BF (Balance Factor): 将二叉树上节点的左子树深度减去右子树深度的值。平衡二叉树的平衡因子只可能是 -1,0,1.
- 最小不平衡树:距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树
多路查找树(B树)
.2-3树
- 其中的每一个节点都具有两个孩子(我们称它为2结点)或三个孩子(我们称它为3结点)
- 一个2结点包含一个元素和两个孩子(或没有孩子)
- 一个3结点包含一大一小两个元素和三个孩子(或没有孩子)
.2-3-4树
- 2-3树概念扩展,包含了4结点的使用。
- 一个4结点包含小中大三个元素和四个孩子(或没有孩子)
.B树
- 一种平衡的多路查找树。并不一定是二叉的。
- 2-3树和2-3-4树都是B树的特例。
- 结点最大的孩子数目称为B树的阶(order)。2-3树是3阶B树,2-3-4树是4阶B树
- 一个m阶的B树具有如下属性:
- 如果根节点不是叶节点,则其至少有两颗子树。
- 所有叶子节点都位于同一层次。
- 每一个非根的分支结点都有k-1个元素和k个孩子。
- 大话数据结构P350
.B+树
一个m阶的B+树和m阶的B树的差异在于:
- 有n棵子树的结点中包含有n个关键字。
- 所有的叶子结点包含全部关键字信息,及指向含这些关键字记录的指针,叶子节点本身依关键字的大小自小而大顺序链接;
- 所有分支结点可以看成是索引,结点中仅含有其子树中的最大(或最小)关键字。
- 特点:
- 如果要随机查找,我们就从根节点触发,与B树的查找方式相同,只不过即使在分支结点找到了待查找的关键字,它也只是用来索引的,不能提供实际记录的访问,还是需要到达包含此关键字的终端节点。
- 如果我们是需要从最小关键字进行从小到大的顺序查找,我们就可以从最左侧的叶子节点出发,不经过分支结点,而是沿着指向下一叶子的指针就可遍历所有的关键字。
- B+树的结构特别适合带有范围的查找
.散列表查找
散列函数的构造方法
-
直接定址法: 简单、均匀,也不会冲突,适合查找表较小且连续的情况。
-
数字分析法: 适合处理关键字位数较大的情况
-
平方取中法: 适合不知道关键字的分布,而位数又不是很大的情况。
-
折叠法: 将将关键字从左到右分割成位数相等的几部分(最后一部分可短),然后将这几部分叠加求和,并安散列表表长,取后几位作为散列地址。折叠法事先不需要知道关键字的分布,适合关键字位数较多的情况。
-
除留余数法※: 最常用的构造散列函数方法。公式为:f(key) = key mod p (p <=m). mod 是求余数的意思。根据经验:若散列表的表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因数的合数。
-
随机数法:取关键字的随机函数值为他的散列地址。也就是f(key)=random(key).当关键字的长度不等时,采用这种方法构造散列函数是比较合适的。
-
参考因素:
-
计算散列地址所需的时间。
-
关键字的长度。
-
散列表的大小
-
关键字的分布情况。
-
记录查找的频率。
处理散列冲突的方法
- 开发定址法。一旦发生冲突,就寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
- 再散列函数法
- 链地址法
- 公共溢出区法
排序
- 冒泡排序(优化)
- 简单选择排序
- 直接插入排序
- 以上三种属于简单算法 以下四种属于改进算法
- 希尔排序(增量序列最后一个必须是1) 不稳定
- 堆排序
- 归并排序
- 快速排序