1.树的结构
- 不对外暴露创建Node实例的方法
- 外部只管操作树
- 树具有
let Tree = function(){
let Node = function(value){
this.value = value
this.left = null
this.right = null
}
// 根节点 设置为私有变量
let root = null
this.insert = function(value){}
this.search = function (value) { }
this.remove = function (value) { }
this.traverse = function (value) { }
//测试方法
this.getRoot = function () {
return root
}
}
2.递归
- 递归的出口
3.插入节点
两种情况:
- 树为空null:插入的设置为根节点
- 树不为空:比较确定位置
//插入节点
let insertNode = function(node,newNode){
if(newNode.value > node.value){
//判断右节点是否为空
if (node.right == null){
node.right = newNode
}else{
insertNode(node.right,newNode)
}
} else if (newNode.value < node.value){
//判断左节点是否为空
if (node.left == null) {
node.left = newNode
} else {
insertNode(node.left,newNode)
}
}
}
this.insert = function (value) {
//新节点
let newNode = new Node(value)
// 树为空
if(root == null){
root = newNode
}else{
insertNode(root, newNode)
}
}
- 测试:
let t = new Tree
t.insert(8)
t.insert(2)
t.insert(3)
t.insert(9)
- 结果
- 使用了递归插入节点
4.树的遍历
- 可以使用回调函数对树中的节点进行相关操作
//打印树种的节点 需要考虑到顺序问题
var printNode = function(value){
console.log('NodeValue - ',value);
}
t.traverse(printNode)
- 必须递归遍历每一个节点
//遍历节点
let traverse = function(node,callback){
//设置递归出口
if(node == null) return
traverse(node.left,callback)
traverse(node.right,callback)
callback(node.value)
}
this.traverse = function (callback) {
traverse(root, callback)
}
- 得到的遍历结果如下:
- 问题:为什么顺序是上述这样?需要分析递归栈
- 若调换回调函数的顺序,相应的遍历的顺序也会发生变化
- 主要还是考虑到递归栈的分析
- 查找文件适合后序遍历
if(node == null) return
// callback(node.value) 8 2 3 9 前序遍历
traverse(node.left,callback)
// callback(node.value) 2 3 8 9 中序遍历
traverse(node.right,callback)
// callback(node.value) 3 2 8 9 后序遍历
5.最小值和最大值方法
let minNode = function(node){
// 若树为空
if(node === null) return null
while(node && node.left){
node = node.left
}
return node.value
}
this.min = function(){
return minNode(root)
}
let maxNode = function (node) {
// 若树为空
if (node === null) return null
while (node && node.right) {
node = node.right
}
return node.value
}
this.max = function () {
return maxNode(root)
}
6.移除节点
- 树结构最头疼的方法
- 替换右侧子树的最小节点
- 原理:重新构建树,先找再删
let findMinNode = function (node) {
if (node == null) return
while (node && node.left) {
node = node.left
}
return node
}
let removeNode = function (node, value) {
if (node == null) return
if(value < node.value){
// 往左边走
node.left = removeNode(node.left,node.value)
return node
} else if (value > node.value){
// 往右边走
node.right = removeNode(node.right, node.value)
return node
}else{
// 找到待删节点,执行删除操作
// 1.若为叶节点
if(node.left == null && node.right == null){
node = null
return node
}
// 2.只有一个子节点
if(node.left == null && node.right){
return node.right
}else if(node.right == null && node.left){
return node.left
}
//3.存在两个子节点
if(node.right && node.left){
//获取右边子树的最小节点
let aux = findMinNode(node.right)
node.value = aux.value
node.right = removeNode(node.right,aux.value)
return node
}
}
}
this.remove = function (value) {
root = removeNode(root, value)
}