树是一种分层数据的抽象模型。现实生活中最常见的树的例子是家谱,或是公司的组织架构图,如下图所示:
一个树结构包含一系列存在父子关系的节点。每个节点都有一个父节点(除了顶部的第一个节点)以及零个或多个子节点
// 生成构造类
function BinarySearchTree() {
// 静态类,生成节点
BinarySearchTree.createNode = function(key) {
this.key = key
this.left = null
this.right = null
}
this.root = null
}
// 二叉树插入
BinarySearchTree.prototype.insert = function(key) {
const node = new BinarySearchTree.createNode(key)
if (!this.root) {
this.root = node
return true
}
let cur = this.root
while (cur) {
// 小于 = left
if (key < cur.key) {
if (cur.left) {
cur = cur.left
} else {
cur.left = node
return true
}
} else {
if (cur.right) {
cur = cur.right
} else {
cur.right = node
return true
}
}
}
}
// 二叉树搜索
BinarySearchTree.prototype.search = function(key) {
let cur = this.root
while (cur) {
if (key === cur.key) {
return true
}
// 小于 = left
if (key < cur.key) {
cur = cur.left
} else {
cur = cur.right
}
}
return false
}
const bst = new BinarySearchTree()
bst.insert(47)
bst.insert(15)
bst.insert(78)
bst.insert(82)
bst.insert(57)
bst.insert(87)
bst.insert(76)
bst.insert(65)
bst.insert(94)
bst.insert(80)
console.log(bst.root)
bst.search(15)
想到的第一个方式就是先序遍历,然后翻看了一下书,遍历有三种方式,真是让人头大
// 这种叫先序遍历
BinarySearchTree.prototype.preOrderTraverseNode = function(callback) {
// 从上到下 从左到右
if (this.root) node1(this.root)
function node1(node) {
if (node === null) return
// 从上往下从左往右先输出节点再遍历
callback && callback(node.key)
if (node.left) node1(node.left)
if (node.right) node1(node.right)
}
}
// 中序遍历
BinarySearchTree.prototype.inOrderTraverse = function(callback) {
inOrderTraverseNode(this.root, callback)
function inOrderTraverseNode(node, callback) {
if (node !== null) {
inOrderTraverseNode(node.left, callback)
callback && callback(node.key)
inOrderTraverseNode(node.right, callback)
}
}
}
// 后序遍历
BinarySearchTree.prototype.postOrderTraverse = function(callback) {
postOrderTraverseNode(this.root, callback)
function postOrderTraverseNode(node, callback) {
if (node !== null) {
//{2}
postOrderTraverseNode(node.left, callback)
postOrderTraverseNode(node.right, callback)
callback && callback(node.key)
}
}
}
BinarySearchTree.prototype.min = function() {
let cur = this.root
if (!cur) return null
while (cur.left) {
cur = cur.left
}
return cur.key
}
BinarySearchTree.prototype.max = function() {
let cur = this.root
if (!cur) return null
while (cur.right) {
cur = cur.right
}
return cur.key
}
删除略复杂,需要考虑以下三种情况
首先需要分析:如果删除了节点,那需要找一个元素来替代
假设删除的节点是15,此时需要 15的 右边20 下找出最小的节点 -> 18 把18提取到15的位置,此时左边的的节点依旧是小于18,右边大于18
假设删除的节点是12,此时直接把 13.left = null 现在假设树种已经没有12这个节点,这时如果再删除13,直接把14这个节点提上去替换13
BinarySearchTree.prototype.remove = function (key) {
if (!this.root) return null
let cur = this.root,
prev = null,
flag = 'left'
while (cur.key !== key) {
prev = cur
if (cur.key < key) {
cur = cur.right
flag = 'right'
} else {
flag = 'left'
cur = cur.left
}
// 如果往下查找已经是null了 pass
if (cur === null) return null
}
// 如果删除的是一颗缺少左右节点的树
if (cur.left === null) {
prev[flag] = cur.right;
return cur
} else if (cur.right === null) {
prev[flag] = cur.left;
return cur
}
// 删除的是一个叶子节点,没有左右节点
if (cur.left === null && cur.right === null) {
prev[flag] = null
return cur
}
// 如果删除的是一颗子树,则从右儿子里取出最小的一个
// 删除当前子树的顶点,然后把找出的节点替换到当前
if (cur.left !== left && cur.right !== null) {
// 如果左右节点都不为空
let _cur = cur.right,
_prev = cur
while (_cur.left) {
_prev = _cur
_cur = _cur.left
}
// 找到最下面的左子树之后需要处理原来左子树的数据 它只可能有右子树的节点,不可能有左子树的节点
_prev.left = _cur.right
// 原来左子树
_cur.left = cur.left
// 原来右子树
_cur.right = cur.right
// 替换原来节点
prev[flag] = _cur
return _cur
}
}