JavaScript实现树结构(二)
一、二叉搜索树的封装
二叉树搜索树的基本属性:
如图所示:二叉搜索树有四个最基本的属性:指向节点的根(root),节点中的键(key)、左指针(right)、右指针(right)。
所以,二叉搜索树中除了定义root属性外,还应定义一个节点内部类,里面包含每个节点中的left、right和key三个属性:
//封装二叉搜索树
function BinarySearchTree(){
//节点内部类
function Node(key){
this.key = key
this.left = null
this.right = null
}
//属性
this.root = null
}
二叉搜索树的常见操作:
- insert(key):向树中插入一个新的键;
- search(key):在树中查找一个键,如果节点存在,则返回true;如果不存在,则返回false;
- inOrderTraverse:通过中序遍历方式遍历所有节点;
- preOrderTraverse:通过先序遍历方式遍历所有节点;
- postOrderTraverse:通过后序遍历方式遍历所有节点;
- min:返回树中最小的值/键;
- max:返回树中最大的值/键;
- remove(key):从树中移除某个键;
1.插入数据
实现思路:
- 首先根据传入的key创建节点对象;
- 然后判断根节点是否存在,不存在时通过:this.root = newNode,直接把新节点作为二叉搜索树的根节点。
- 若存在根节点则重新定义一个内部方法insertNode()用于查找插入点。
//insert方法:对外向用户暴露的方法 BinarySearchTree.prototype.insert = function(key){ //1.根据key创建节点 let newNode = new Node(key) //2.判断根节点是否存在 if (this.root == null) { this.root = newNode //根节点存在时 }else { this.insertNode(this.root, newNode) } }
内部方法insertNode()的实现思路:
根据比较传入的两个节点,一直查找新节点适合插入的位置,直到成功插入新节点为止。
当newNode.key < node.key向左查找:
-
情况1:当node无左子节点时,直接插入:
-
情况2:当node有左子节点时,递归调用insertNode(),直到遇到无左子节点成功插入newNode后,不再符合该情况,也就不再调用insertNode(),递归停止。
当newNode.key >= node.key向右查找,与向左查找类似:
-
情况1:当node无右子节点时,直接插入:
-
情况2:当node有右子节点时,依然递归调用insertNode(),直到遇到传入insertNode方法的node无右子节点成功插入newNode为止:
insertNode()代码实现:
//内部使用的insertNode方法:用于比较节点从左边插入还是右边插入 BinarySearchTree.prototype.insertNode = function(node, newNode){ //当newNode.key < node.key向左查找 /*----------------------分支1:向左查找--------------------------*/ if(newNode.key < node.key){ //情况1:node无左子节点,直接插入 /*----------------------分支1.1--------------------------*/ if (node.left == null) { node.left = newNode //情况2:node有左子节点,递归调用insertNode(),直到遇到无左子节点成功插入newNode后,不再符合该情况,也就不再调用insertNode(),递归停止。 /*----------------------分支1.2--------------------------*/ }else{ this.insertNode(node.left, newNode) } //当newNode.key >= node.key向右查找 /*-----------------------分支2:向右查找--------------------------*/ }else{ //情况1:node无右子节点,直接插入 /*-----------------------分支2.1--------------------------*/ if(node.right == null){ node.right == newNode //情况2:node有右子节点,依然递归调用insertNode(),直到遇到无右子节点成功插入newNode为止 /*-----------------------分支2.2--------------------------*/ }else{ this.insertNode(node.right, newNode) } } }
过程详解:
为了更好理解以下列二叉搜索树为例:
想要上述的二叉搜索树(蓝色)中插入数据10:
- 先把key = 10 传入insert方法,由于存在根节点 9,所以直接调用insetNode方法,传入的参数:node = 9,newNode = 10;
- 由于10 > 9,进入分支2,向右查找适合插入的位置;
- 由于根节点 9 的右子节点存在且为 13 ,所以进入分支2.2,递归调用insertNode方法,传入的参数:node = 13,newNode = 10;
- 由于 10 < 13 ,进入分支1,向左查找适合插入的位置;
- 由于父节点 13 的左子节点存在且为11,所以进入分支1.2,递归调用insertNode方法,传入的参数:node = 11,newNode = 10;
- 由于 10 < 11,进入分支1,向左查找适合插入的位置;
- 由于父节点 11 的左子节点不存在,所以进入分支1.1,成功插入节点 10 。由于不符合分支1.2的条件所以不会继续调用insertNode方法,递归停止。
测试代码:
//测试代码
//1.创建BinarySearchTree
let bst = new BinarySearchTree()
//2.插入数据
bst.insert(11);
bst.insert(7);
bst.insert(15);
bst.insert(5);
bst.insert(9);
console.log(bst);
应得到下图所示的二叉搜索树: