回忆下,我们说的链表是一维结构,它的查找节点时间复杂度是O(n)的,正式因为这个原因的话,后来就出现了跳表一样的结构,加上了更快的索引,比如从头指针直接跨到第二个节点,注意,这里涉及一个理念,前面也提到过,如果我们想加速,关键点是升维,常见的二维的数据结构有树和图。
链表结构:
如果链表的节点有多个next指针,指向多个节点,那么它就变成了一个数🌲状结构了。接下来,我们认识一种新的数据结构 - 树。
Tree 树
树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树.
首先它有个根节点(root),B节点和C节点都是它的子树🌲,我们称B和C为左子树和右子树,它们是彼此的兄弟节点,同时B和C节点又为它们子节点的父节点。树还分了不同的层,root节点为第0层,B和C为第一层。
定义树节点
function TreeNode(val) {
this.val = val;
this.left = this.right = null;
}
Binary Tree 二叉树
现实中用的更多的还是二叉树,二叉树的子节点只有2个。
树的延伸 - Graph 图(简介)
那么树和图最关键的一个差别是什么呢,就是看有没有环装结构,如果节点本身的话,只连到新的儿子节点,永远不会走回去,如下图,如果F节点有个next指针指向了E,或者指向了A,或者指向了它的父节点C的话,那么就相当于形成了一个环,这是我们按照定义不称为树,而是叫做图。

但是在特殊情况下,你也可以理解为Linked list就是特殊化的Tree(多个next指针的链表), Tree就是特殊化的Graph(没有环的🌲)。
树的遍历
- 前序遍历(pre-order):根-左-右
- 中序遍历(In-order):左-根-右
- 后序遍历(Post-order):右根左
/**
* @descripe 前序遍历
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function (root) {
let arr = [];
preorder(root, arr);
return arr;
}
function preorder(root, arr){
if (!root) {
return;
}
if (root){
arr.push(root.val); // 当前节点收集
preorder(root.left, arr); // 左节点搜集
preorder(root.right, arr); // 右节点搜集
}
}
这么看来,我们要想查找某个节点的值,时间复杂度还是O(n)的,因为看来需要整个遍历一下,它和一个链表基本上没有太大的区别,那么树到底有什么优势呢?一般来说,树的结构我们会把它变得更加有序,这就好像你的宿舍或者你的办公室、或者你的电脑里面,经常使用的东西,把它排的相对有序的话,那你查找和维护起来就会更加有效,二叉树也是这样,这就是我们接下来要介绍的二叉搜索树。
二叉搜索树 (Binary Search Tree)
二叉搜索树,也称为二叉排序树、有序二叉树(Ordered Binary Tree)、排序二叉树(Sorted Binary Tree),是指一棵空树或者具有下列性质的二叉树:
- 左子节点树上所有节点的值均小于它的根节点上的值
- 右子节点树上所有节点的值均大于它的根节点上的值
- 以此类推,左、右子树也分别为二叉查找树。(这就是重复性!)
二叉搜索树的中序遍历就是升序遍历!!
二叉搜索树常见操作
- 查询
- 插入新节点(创建)
- 删除
它的查询和插入都是O(logn)了,所以就相当于加速了。