树
树是一种十分重要的数据结构。树被描述为一种分层数据抽象模型,常用来描述数据间的层级关系和组织结构。树也是一种非顺序的数据结构。下图展示了树的定义:
如上图所示,一棵完整的树包含一个位于树顶部的节点,称之为根节点(11),它没有父节点。
树中的每一个元素都叫做一个节点,节点分为内部节点(图中显示为黄色的节点)和外部节点(图中显示为灰色的节点),至少有一个子节点的节点称为内部节点,没有子元素的节点称为外部节点或叶子节点。
一个节点可以有祖先(根节点除外)和后代。子树由节点本身和它的后代组成,如上图中三角虚框中的部分就是一棵子树。节点拥有的子树的个数称之为节点的度,如上图中除叶子节点的度为0外,其余节点的度都为2。
从根节点开始,根为第1层,第一级子节点为第2层,第二级子节点为第3层,以此类推。树的高度(深度)由树中节点的最大层级决定(上图中树的高度为4)。
在一棵树中,具有相同父节点的一组节点称为兄弟节点,如上图中的3和6、5和9等都是兄弟节点。
二叉树
二叉树中的节点最多只能有两个子节点,一个是左子节点,一个是右子节点。左右子节点的顺序不能颠倒。因此,二叉树中不存在度大于2的节点。
二叉搜索树(BST——Binary Search Tree)是二叉树的一种,它规定在左子节点上存储小(比父节点)的值,在右子节点上(比父节点)存储大(或等于)的值。上图就是一个二叉搜索树。
下面我们重点来看一下二叉搜索树的实现。
二叉树的实现
节点结构
为了方便清晰 我们分别用 left 表示左节点, right 表示右节点
- left 左子节点
- el 节点元素
- right 右子节点
class Node {
constructor(el) {
this.left = null; // 左节点
this.el = el;
this.right = null // 右节点
}
}
树结构
- 插入节点
- 查找节点
- 中序遍历所有节点
- 先序遍历所有节点
- 后序遍历所有节点
- 最小节点
- 最大节点
- 移除节点
class BinarySearchTree {
constructor () {
this.root = null;
}
// 向树中插入一个节点
insert (key) {}
// 在树中查找一个节点
search (key) {}
// 通过中序遍历方式遍历树中的所有节点
inOrderTraverse () {}
// 通过先序遍历方式遍历树中的所有节点
preOrderTraverse () {}
// 通过后序遍历方式遍历树中的所有节点
postOrderTraverse () {}
// 返回树中的最小节点
min () {}
// 返回树中的最大节点
max () {}
// 从树中移除一个节点
remove (key) {}
}
插入节点
节点的顺序遵循从左节点到右节点 左子节点上存储小(比父节点)的值,在右子节点上(比父节点)存储大(或等于)的值
insert(key) {
const node = new Node(key);
if(this.root === null) {
this.root = node
} else {
}
}
查找节点
查找逻辑
- 如果树为空 直接返回空
- 判断查询值 于当前的树的根节点大小
- 如果小于 节点 进行节点左侧递归判断
- 如果大于 节点 进行节点右侧递归判断
- 相等 就 返回该节点
search (key) {
return this.searchNode(this.root, key)
}
searchNode(node, key) {
if (node === null) return null;
// 如果 查询元素 小于当前元素 左侧查询
if (key < node.el) return this.searchNode(node.left, key);
// 如果大于 右侧查询
else if (key > node.el) return this.searchNode(node.right, key);
else return node;
}
中序遍历
遍历逻辑
inOrderTraverse (callback) {
this.inOrderTraverseNode(this.root, callback)
}
inOrderTraverseNode (node, callback) {
if (node !== null) {
this.inOrderTraverseNode(node.left, callback);
callback(node.el);
this.inOrderTraverseNode(node.right, callback);
}
};
先序遍历
遍历规则
preOrderTraverse (callback) {
this.preOrderTraverseNode(this.root, callback)
}
preOrderTraverseNode (node, callback) {
if (node !== null) {
callback(node.el);
this.preOrderTraverseNode(node.left, callback);
this.preOrderTraverseNode(node.right, callback);
}
};
后序遍历
遍历规则
postOrderTraverse (callback) {
this.postOrderTraverseNode(this.root, callback)
}
postOrderTraverseNode (node, callback) {
if (node !== null) {
this.postOrderTraverseNode(node.left, callback);
this.postOrderTraverseNode(node.right, callback);
callback(node.el);
}
};
最小节点
- 最小节点永远都是树的最左侧节点
if(this.root === null) return null;
let node = this.root
while (node.left) {
node = node.left;
}
return node
最大节点
- 最大节点永远是最右侧的节点
if(this.root === null) return null;
let node = this.root
while (node.right) {
node = node.right;
}
return node
移除节点
remove (key) {
if(this.root === null) return null;
}