js树的实现

120 阅读2分钟

相关术语

  • 根节点:树顶部的节点
  • 内部节点:至少有一个子节点的节点
  • 外部节点或叶节点:没有子元素的节点
  • 子树:由节点和它的后代构成
  • 深度:节点的深度取决于它的祖先节点的数量
  • 高度:取决于所有节点深度的最大值

image.png

二叉树和二叉搜索树

二叉树

  • 节点最多只能有两个子节点,一个是左侧子节点,另一个是右侧子节点。

二叉搜索树

  • 是二叉树的一种
  • 只允许在左侧节点存储(比父节点)小的值,在右侧节点存储(比父节点)大的值。

代码实现

创建二叉树的每个节点

export class Node{
    constructor(key){
        this.key = key //每个节点的值
        this.left = null //左侧节点
        this.right = null //右侧节点
    }
}

创建树类及其各类方法

export default class Tree {
    constructor(){
        this.root = null  //根节点
    }
    //插入节点
    insert(value){}
    //查找节点
    search(value){}
    //通过中序遍历方式遍历所有节点。
    inOrderTraverse(callback){}
    
    //通过先序遍历方式遍历所有节点。
    preOrderTraverse(callback){}
    
    //返回树中最小的值/键。
    min(){}
    //返回树中最大的值/键。
    max(){}
    //移除节点
    remove(value){}
    
    //帮助我们找到新节点应该插入的正确位置。
    insertNode(node,newNode){
        //新插入的节点值大于当前节点的值,向右边节点走
        if(newNode.key > node.key){
            //右边节点没有值的时候,直接将节点插如右边节点 
            if(node.right === null){
                node.right = newNode
            }else{
                //右边节点有值的时候,继续递归查找后面的节点
                this.insertNode(node.right,newNode)
            }
        }else if(newNode.key < node.key){
        //新插入的节点值小于当前节点的值,向左边节点走
            if(node.left === null){
                //左边节点没有值的时候,直接将节点插如左边节点
                node.left = newNode
            }else{
                //左边节点有值的时候,继续递归查找后面的节点
                this.insertNode(node.left,newNode)
            }
        }
    }
    //获取当前树
    getRoot(){
        return this.root
    }
}

添加元素

insert(value){
    let newNode = new Node(value)
    //当前树是空的
    if(this.root === null){
        this.root = newNode
    }else{
        this.insertNode(this.root,newNode)
    }
}

通过中序遍历方式遍历所有节点。

inOrderTraverse(callback){
    this.inOrderTraverseNode(this.root,callback)
}


//中序递归遍历,node需要遍历的节点,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)
}

//先序递归遍历,node需要遍历的节点,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)
}

//通过后序遍历方式遍历所有节点。
postOrderTraverse(callback){}
//后序递归遍历,node需要遍历的节点,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){
    let current = node
    while(current !== null && current.left !== null){
        current = current.left
    }
    return current
}

返回树中最大的值/键。

max(){
    return this.maxNode(this.root)
}
maxNode(node){
    let current = node
    while(current !== null && current.right !== null){
        current = current.right
    }
    return current
}

查找节点

search(value){
    return this.searchNode(this.root,value)
}
searchNode(node,value){
    if(node === null) return false
    if(value > node.key){//查找的节点大于当前节点
        this.searchNode(node.right,value)
    }else if(value < node.key){//查找的节点小于当前节点
        this.searchNode(node.left,value)
    }else{
        return true
    }
}

移除节点

  • 删除的原理是重新构建树
  • 有两个子节点时将移除的节点替换为右侧子树的最小节点
remove(value){
    this.root = this.removeNode(this.root,value)
}
removeNode(node,value){
    if(root !== null){
        //查找需要删除的节点
        if(value > node.key){
            this.removeNode(node.right,value)
        }else if(value < node.key){
            this.removeNode(node.left,value)
        }else{
            //查找到要删除的节点
            if(node.left === null && node.right === null){
                //要删除的节点没有子节点
                node = null
                return node
            }
            
            //只有一个子节点
            if(node.left === null && node.right !== null){
                //只有右边的子节点
                return node.right
            }else if(node.left !== null && node.right === null){
                //只有左边的子节点
                return node.left
            }else if(node.left !== null && node.right !== null){
                //有两个子节点
                let minNode = this.findMinNode(node.right)//查找右侧子节点的最小节点
                node.key = minNode.key/将查找到的节点的值赋给当前节点
                node.right = this.removeNode(node.right,minNode.key)//删除查找到的节点
            } 
        }
    }
}

//查找右侧最小的子节点
findMinNode(node){
    let current = node
    while(current && current.left){
        current = current.left
    }
    return cuurent
}