文章目录
一 . 树
1.二叉树
- 二叉树的性质
- 在二叉树的第 i 层上,最多有 2^(i-1) 个结点
- 深度为 k 的二叉树至多有 2^k - 1 个结点
- 对让你和一棵二叉树 T,如果其叶子结点数为 n0,度为2 的结点数为 n2,则
n0 = n2 + 1
2. 说一下多路查找树(B树)
-
多路查找树
每一个结点的孩子数可以多于两个,且每一个结点处可以存储多个元素。 -
2-3 树
2-3 树是这样的一棵多路查找树:其中每一个结点都具有两个孩子或三个孩子。一个2结点包含两个孩子和一个元素,且左子树包含的元素小于该元素,右子树包含的元素大于该元素。
一个3结点包含一大一小两个元素和三个孩子。如果某个3结点有孩子的话,左子树包含小于较小元素的元素,右子树包含大于较大元素的元素,中间子树包含介于两元素之间的元素。
并且2-3树中的所有叶子均在同一层。
-
2-3-4 树
2-3-4 树包括了4结点的使用。一个4结点包含了小中大三个元素和4个孩子。左子树包含小于最小元素的元素;第二子树包含大于最小元素,小于第二元素的元素;第三子树包含大于第二元素小于最大元素的元素;右子树包含大于最大元素的元素。
1.1 B树
B树是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例。结点最大的孩子的数目称为B树的阶。 2-3树是3阶B树,2-3-4树是4阶B树。
一个m阶B树有如下属性:
- 如果根节点不是叶结点,则至少右两棵子树。
- 每个非根的分支结点都有k-1个元素和k个孩子。
-
B树的查找
是一个顺着指针查找结点和在结点中查找关键字的过程。
比如我们要读取7,首先从外存中读取3、5、8三个元素到内存,发现7不在当中,但在5和8之间,因此就通过A2再读取外存中的6、7结点,查找到了所要的元素。我们可以看到,如果树的高度或者分支越多,磁盘的I/O就会越频繁,所以使用B树可以很好减少磁盘的I/O,所以用作数据库的索引。 -
B树是如何做到内存与外存交换数据频繁这个缺陷的呢?
1.2 m 阶 B 树的插入操作
B-树的插入操作
插入操作是指插入一条记录,即(key, value)的键值对。如果B树中已存在需要插入的键值对,则用需要插入的value替换旧的value。若 B 树中不存在这个 key 值,则一定是在叶子节点中进行插入操作。
- 根据要插入的key的值,找到叶子结点并插入。
- 判断当前结点key的个数是否小于等于m-1,若满足则结束,否则进行第3步。
- 若当前结点key的个数等于m-1,以结点中间的key为中心分裂成左右两部分,然后将这个中间的key插入到父结点中,这个key的右子支指向分裂后的右半部分,然后将当前结点指向父结点,继续进行第3步。
1.3 B+ 树
B树虽然有诸多的好处,但是还是具有很多的缺陷的。对于树结构来说,我们可以通过中序遍历来顺序查找树中的元素,这一切都是在内存中进行的。但是在B树中,每个结点的访问也就意味着我们必须在硬盘的页面之间进行多次访问。
为了能够解决元素遍历等基本问题,我们在B树的基础上加上了新的元素组织方式,这就是B+树。
B+树介绍
B+树的特征:
- 有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
- 所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
- 所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。
B+树的优势:
4. 单一节点存储更多的元素,使得查询的IO次数更少。
5. 所有查询都要查找到叶子节点,查询性能稳定。
6. 所有叶子节点形成有序链表,便于范围查询。
1.4 B 树和 B+ 树的区别
- 关键字的数量不同:B+树有m个关键字,也有m个叶子结点,关键字只是用来存储索引。B树虽然也有m个子节点,但是其只拥有m-1个关键字。
- 存储的位置不同:B+树中的数据都存储在叶子结点上,也就是其所有叶子结点的数据组合起来就是完整的数据,但是B树的数据存储在每一个结点中,并不仅仅存储在叶子结点上。
- 查询不同:B树在找到具体的数值以后,则结束,而B+树则需要通过索引找到叶子结点中的数据才结束,也就是说B+树的搜索过程中走了一条从根结点到叶子结点的路径。
为了便于说明,我们先定义一条数据记录为一个二元组[key,data],key为记录的键值,key唯一;data为数据记录除key外的数据。
- B 树
每个结点都存储 key 和 value,所有节点组成这棵树,并且叶子节点指针为null。
- B+ 树
只有叶子结点存储 data,叶子结点包含这棵树的所有键值
后来,在B+树上增加了顺序访问的指针,也就是在每个叶子结点增加了一个指向相邻叶子结点的指针,这样一棵树成了数据库系统实现索引的首选数据结构。
在MySQL中,最常用的两个存储引擎是MyISAM和InnoDB,它们对索引的实现方式是不同的。
-
MyISAM
data存的是数据地址。索引是索引,数据是数据。索引放在XX.MYI文件中,数据放在XX.MYD文件中,所以也叫非聚集索引。
-
InnoDB
data存的是数据本身。索引也是数据。数据和索引存在一个XX.IDB文件中,所以也叫聚集索引。
为什么说B+树比B 树更适合实际应用中操作系统的文件索引和数据库索引? -
与B树比较
一棵m阶的B+树和m阶的B树的异同点在于:
所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。(而B 树的叶子节点并没有包括全部需要查找的信息)
所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。(而B 树的非终节点也包含需要查找的有效信息) -
B+树的磁盘读写代价更低
B+树的内部结点并没有指向关键字具体信息的指针。因此其内部结点相对B 树更小。如果把所有同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的需要查找的关键字也就越多。相对来说IO读写次数也就降低了。
举个例子,假设磁盘中的一个盘块容纳16bytes,而一个关键字2bytes,一个关键字具体信息指针2bytes。一棵9阶B-tree(一个结点最多8个关键字)的内部结点需要2个盘块。而B+树内部结点只需要1个盘块。当需要把内部结点读入内存中的时候,B 树就比B+树多一次盘块查找时间(在磁盘中就是盘片旋转的时间)。 -
B+树的查询效率更加稳定
由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
红黑树
-
红黑树的主要特征
(1)每个节点要么是黑色,要么是红色
(2)根节点是黑色
(3)每个叶子节点(NIL)是黑色
(4)如果一个节点是红色的,则它的子节点必须是黑色的,也就是说父子节点不能同时为红色
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。(这一点是平衡的关键)
(6)说简单也简单,其实就是一颗比较平衡的又红又黑的二叉树嘛 -
红黑树的各种操作的时间复杂度是多少?
能保证在最坏情况下,基本的动态几何操作的时间均为O(logn) -
红黑树相比于BST和AVL树有什么优点?
红黑树是牺牲了严格的高度平衡的优越条件为代价,它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。红黑树能够以O(log2 n)的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。当然,还有一些更好的,但实现起来更复杂的数据结构能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较“便宜”的解决方案。
相比于BST,因为红黑树可以能确保树的最长路径不大于两倍的最短路径的长度,所以可以看出它的查找效果是有最低保证的。在最坏的情况下也可以保证O(logN)的,这是要好于二叉查找树的。因为二叉查找树最坏情况可以让查找达到O(N)。
红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高,所以在插入和删除中所做的后期维护操作肯定会比红黑树要耗时好多,但是他们的查找效率都是O(logN),所以红黑树应用还是高于AVL树的. 实际上插入 AVL 树和红黑树的速度取决于你所插入的数据.如果你的数据分布较好,则比较宜于采用 AVL树(例如随机产生系列数),但是如果你想处理比较杂乱的情况,则红黑树是比较快的
二. 图
2.1 图的存储结构
邻接矩阵
图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中定点信息,一个二维数组(称为邻接矩阵)存储图中边或弧的信息。
- 我们要知道某个定点的度,其实就是这个定点 vi 在邻接矩阵中第 i 行的元素之和。比如 v1 的度就是 2。
邻接表
邻接矩阵是一个不错的图的存储结构,但是我们也发现,对于边数相对顶点较少的图,这种存储结构会对存储空间造成极大的浪费。
我们把这种将数组和链表相结合的存储方式称为邻接表。
2.2 图的遍历
从图中某个顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历。
深度优先遍历(DFS)
它从图的某个顶点 v 出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直到图中所有和 v 有路径相同的顶点都被访问到。
广度优先遍历(BFS)
广度优先遍历类似于树的层次遍历。
2. 排序
- 选择排序是不稳定的,在排序过程中,需要找到最小值与已排好序的最后一位进行交换,此时可能会打乱顺序。如:1,2,3,7,5,7,4 进行下一趟排序变成了:1,2,3,4,5,7,7。此时两个 7 的位置已经换了顺序。
- 快速排序虽然总体的平均效率是最好的,但也不是任何时候都是最优的算法。比如数组本身已经排好序了,而每一轮排序的时候都以最后一个数字作为排序标准,此时快速排序的效率只有O(n^2)。
3. 哈希表
3.1 哈希函数
- 我们通过某个函数 f,使得 存储位置 = f(关键字),那样我们就可以通过查找关键字而不需要比较就可以获取需要的记录的位置了。
- 散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系 f,使得每个关键字 key 对应一个存储位置 f(key)。
- 这里我们称这种对应关系 f 为散列函数,又称为哈希(Hash)函数。
3.2 散列函数的构造方法
- 直接定址法
f(key) = a*key + b - 除留取余法
f(key) = key mod p
3.3 冲突
两个关键字key1 不等于 key2,但是有f(key1) 等于 f(key2),这种现象我们称为冲突。
- 开放定址法
所谓开放定址法就是一旦发生了冲突,就去寻找下一个空的散地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
fi(key) = (f(key) + di) mod m
1.1. 线性探测法
1.2 二次探测法