二叉树
二叉树结构图:
树顶部的节点是根节点,有子元素的节点叫做内部节点,没有子元素的节点叫做外部节点或叶节点
节点和它的子节点称为子树
节点左侧存储小值,节点右侧存储大值
使用js实现二叉树结构,待实现功能如下:
insert(key) 向树中插入新的键
search(key) 在树中查找一个键,如果节点存在,返回true,如果不存在,返回false
inOrderTraverse() 通过中序遍历方式遍历所有节点(由小到大排序)
preOrderTraverse() 通过先序遍历方式遍历所有节点()
postOrderTraverse() 通过后序遍历方式遍历所有节点
min() 返回树中最小的值/键
max() 返回树中最大的值/键
remove(key) 从树中移除某个键
代码(二叉树数据结构会大量运用递归):
class Node{ // 节点 constructor(key){ this.key = key this.left = null this.right = null } } const compare = { Less_Than : -1, Bigger_Than : 1 } function defaultCompare(a, b){ // 比较大小 if(a === b){ return 0 } return a < b? compare.Less_Than : compare.Bigger_Than }
创建二叉树类:
class BinarySearchTree{ // 二叉树 constructor(compareFn = defaultCompare){ this.compareFn = compareFn // 用来比较节点值 this.root = null // 根节点 } }
插入节点:
根据左小右大,通过递归逐级下查,查找待添加节点的位置
insert(key){ if (this.root === null){ this.root = new Node(key) } else{ this._insertNode(this.root, key) } } _insertNode(node, key){ if(this.compareFn(key, node.key) === compare.Less_Than){ if(node.left === null){ node.left = new Node(key) }else{ this._insertNode(node.left, key) } }else{ if(node.right === null){ node.right = new Node(key) }else{ this._insertNode(node.right, key) } } }
查找键是否存在:
和插入节点类似,通过递归,沿着一个方向逐级向下查找,当查到null时,即不存在
search(key){ return this._searchNode(this.root, key) } _searchNode(node, key){ if(node === null){ return false } if(this.compareFn(key, node.key) === compare.Less_Than){ return this._searchNode(node.left, key) } else if(this.compareFn(key, node.key) === compare.Bigger_Than){ return this._searchNode(node.right, key) } else{ return true } }
中序遍历所有节点的大小(由小到大排序):
callback参数传入一个回调函数,用来遍历后执行
例如传入const a = (val) => { console.log(val) }
返回结果是:由小到大依次排列的节点值,例:1,2,3,4,5,6,7,8,9
inOrderTraverse(callback){ this._inOrderTraverseNode(this.root, callback) } _inOrderTraverseNode(node, callback){ if(node != null){ this._inOrderTraverseNode(node.left, callback) callback(node.key) this._inOrderTraverseNode(node.right, callback) } }
先序遍历:
和中序遍历类似,只是遍历结果发生了改变
preOrderTraverse(callback){ this._preOrderTraverseNode(this.root, callback) } _preOrderTraverseNode(node, callback){ if(node !== null){ callback(node.key) 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.key) } }
最小值查询:
直接查找最左侧子树的最左侧节点
min(){ return this._minNode(this.root) } _minNode(node){ if(node !== null && node.left !== null){ return this._minNode(node.left) }else{ return node } }
最大值查询:
直接查找最右侧子树的最右侧节点
max(){ return this._maxNode(this.root) } _maxNode(node){ if(node !== null && node.right !== null){ return this._maxNode(node.right) }else{ return node } }
删除指定节点:
首先,找到待删除节点,若找不到,则返回原树
若找到,分为三种情况:
1.节点为叶节点,即左右节点都为null,直接删掉即可
2.节点为内部节点但左右有一侧为null,则用另一侧节点代替当前节点
3.节点左右侧都有,则需要找到右侧树中的最小值来取代当前节点,然后再对右侧树执行一个末位节点删除,删除掉重复的原替代值
remove(key){ this.root = this._removeNode(key, this.root) } _removeNode(key, node){ if(node === null){ return null } if(this.compareFn(key, node.key) === compare.Less_Than){ node.left = this._removeNode(key, node.left) return node }else if(this.compareFn(key, node.key) === compare.Bigger_Than){ node.right = this._removeNode(key, node.right) return node }else{ // 第一种情况,删除末位节点 if(node.left === null && node.right === null){ node = null return node } // 第二种情况,左或右节点为Null if(node.left === null){ node = node.right return node }else if(node.right === null){ node = node.left return node } // 第三种情况,左右都有子节点 // 找到右侧子树中最小的节点 const newNode = this._minNode(node.right) node.key = newNode.key // 删除右侧子树中最小的节点 this._removeNode(newNode.key, node.right) return node } }