「观感度:🌟🌟🌟🌟🌟」前端
「口味:蚂蚁上树」node
「烹饪时间:10min」git
本文已收录在
Github
github.com/Geekhyt,感谢Star。github
周树人先生曾经说过:学好树,数据结构与算法你就掌握了一半!web
食堂老板(童欧巴):就算咱们做为互联网浪潮中的叶子结点,也须要有不自量力的精神,就算不自量力是自不量力。由于就算终其一辈子只是个普通人,但你总不能为了成为一个普通人而终其一辈子吧。算法
今日菜谱,蚂蚁上树,下面介绍一下演员。数据库
A 是 根节点
。C、D、F、G 是 叶子节点
。A 是 B 和 E 的 父节点
。B 和 E 是 A 的 子节点
。B、E 之间是 兄弟节点
。高度、深度、层
如上图所示。为了方便理解记忆,高度
就是抬头看,深度
就是低头看。与 高度、深度 不一样,层
类比盗梦空间里的楼,楼都是从 1 层开始计算,盗梦空间中的楼颠倒过来,从上往下。数组
每一个节点最多有两个子节点,也就是 左子节点
和 右子节点
。数据结构
叶子节点全都在最底层,除了叶子节点以外,每一个节点都有左右两个子节点。上文中的图就是满二叉树。编辑器
叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,而且除了最后一层,其余层的节点个数都要达到最大。
堆其实就是一种彻底二叉树,通常采用的存储方式是数组。
采用数组存储彻底二叉树,无须像链式存储同样额外存储左右子节点的指针,能够节省内存。
(ABCDEFG)
(CBDAFEG)
(CDBFGEA)
// 前序遍历
const preorderTraversal = function(root) { const result = []; function pushRoot(node) { if (node != null) { result.push(node.val); if (node.left != null) { pushRoot(node.left); } if (node.right != null){ pushRoot(node.right); } } } pushRoot(root); return result; }; 复制代码
// 中序遍历
const inorderTraversal = function(root) { const result = []; function pushRoot(node) { if (node != null) { if (node.left != null) { pushRoot(node.left); } result.push(node.val); if (node.right != null){ pushRoot(node.right); } } } pushRoot(root); return result; }; 复制代码
// 后序遍历
const postorderTraversal = function(root) { const result = []; function pushRoot(node) { if (node != null) { if (node.left != null) { pushRoot(node.left); } if (node.right != null){ pushRoot(node.right); } result.push(node.val); } } pushRoot(root); return result; }; 复制代码
又称二叉排序树、二叉搜索树。
中序遍历二叉查找树,能够输出有序的数据序列,时间复杂度是 O(n)
,很是高效。
二叉查找树中的任意一个节点,左子树中的每一个节点的值,都小于这个节点的值,而右子树节点的值都大于这个节点的值。
在二叉查找树中,查找、插入、删除等不少操做的时间复杂度都跟树的高度成正比。两个极端状况的时间复杂度分别是 O(n)
和 O(logn)
,分别对应二叉树退化成链表的状况和彻底二叉树的状况。
极端状况下复杂度退化并非咱们想要的,因此咱们须要设计一种平衡二叉查找树。平衡二叉查找树的高度接近 logn
,因此查找、插入、删除操做的时间复杂度也比较稳定,是 O(logn)
。
AVL 树
是最早被发明的平衡二叉查找树(以发明者的名字来进行的命名),它定义任何节点的左右子树高度相差不超过 1,而且左右两个子树都是一棵平衡二叉树。
AVL 树
是一种高度平衡的二叉查找树。虽然查找效率高,可是为了维持高度平衡,插入、删除操做时都须要进行调整(左旋,右旋),须要付出很大的维持成本。
红黑树能够被称为“网红树”了,它的出场率相比其余的树要高出一个天际线。与 AVL 树不一样,红黑树只是作到了近似平衡,并非严格意义上的平衡。因此在维护平衡付出的成本上比 AVL 树要低,查找、插入、删除等操做的性能都比较稳定,时间复杂度都是 O(logn)
。
一棵合格的红黑树应该知足:
红黑树在工程上有着大量的应用,由于工程上对性能的稳定性要求是很高的。正由于红黑树的性能比较稳定,它扛起了工程应用上的大旗。
Google、百度一类的搜索引擎强大的关键词提示功能的背后,最基本的原理就 Trie 树
,经过空间换时间,利用字符串的公共前缀,下降查询的时间以提升效率。除此以外,还有不少应用,好比:IP 路由中使用了 Trie 树的最长前缀匹配算法,利用转发表选择路径以及 IDE 中的智能提示等。
Trie 树
是一棵非典型的多叉树模型,它和通常的多叉树不一样,咱们能够对比一下它们结点的数据结构设计。通常的多叉树结点中包含结点值和指向子结点的指针。而 Trie 树的结点中包含的是该结点是不是一个串的结束,以及字母映射表。经过字母映射表咱们能够经过一个父结点来获取它全部子结点的值。
在 Trie 树 中查找字符串的时间复杂度是 O(k)
,k 是要查找的字符串长度。
上图中的 Trie 树由 5 个单词构成,分别是 color、coat、city、hi、hot
。根结点的值为空。
class Trie {
constructor() { this.root = {}; } insert(word) { let curr = this.root; word.split('').forEach(ch => (curr = curr[ch] = curr[ch] || {})); curr.isWord = true; } traverse(word) { let curr = this.root; for (let i = 0; i < word.length; i++) { if (!curr) return null; curr = curr[word[i]]; } return curr; } search(word) { let node = this.traverse(word); return !!node && !!node.isWord; } startsWith(word) { return !!this.traverse(word); } } 复制代码
咱们知道,将索引存储在内容中,查询速度是比存储在磁盘中快的。可是当数据量很大的状况下,索引也随之变大。内存是有限的,咱们不得不将索引存储在磁盘中。那么,如何提高从磁盘中读取的效率就成了工程上的关键之一。
大部分关系型数据库的索引,好比 MySQL、Oracle,都是用 B+ 树来实现的。
B+ 树比起红黑树更适合构建存储在磁盘中的索引。B+ 树是一个多叉树,在相同个数的数据构建索引时,其高度要低于红黑树。当借助索引查询数据的时,读取 B+ 树索引,须要更少的磁盘 IO 次数。
一个 m 阶的 B 树知足以下特征:
1.看到这里了就点个赞支持下吧,你的「赞」是我创做的动力。
2.关注公众号前端食堂,「你的前端食堂,记得按时吃饭」!
3.本文已收录在前端食堂Github
github.com/Geekhyt,求个小星星,感谢Star。