数据结构之二叉树---JS实现

75 阅读1分钟

使用JS实现二叉搜索树、平衡二叉树、红黑树(待实现)、线段树(待实现)。

1-二叉树节点

// ./binary-tree-node.js

module.exports = class BinaryTreeNode {    
    /**     
    *      
    * @param {*} data       
    * @param {function(d1: *, d2: *)} [compare]     
    */    
    constructor(data, compare) {        
        this.data = data || null        
        this.left = null        
        this.right = null        
        this.parent = null        
        this.compare = compare || BinaryTreeNode.defaultCompare    
    }    

    /**    
    *     
    * @param {*} d1     
    * @param {*} d2     
    * @returns { number }    
    */    
    static defaultCompare(d1, d2) {        
        if (d1 === d2) return 0        
        return d1 > d2 ? 1 : -1    
    }    
    
    get leftHeight() {        
        if (!this.left) return 0        
        return this.left.height    
    }
    
    get rightHeight() {        
        if (!this.right) return 0        
        return this.right.height    
    }    
    
    get height() {        
        return Math.max(this.leftHeight, this.rightHeight) + 1    
    }    
    
    get grandpa() {        
        if (!this.parent || this.parent.parent) return null        
        return this.parent.parent    
    }    
        
    // for red-black tree to balance    
    get uncle() {        
        if (!this.grandpa) return null  
        if (!this.grandpa.left || !this.grandpa.right) return null 
        if (Object.is(this.parent, this.grandpa.left)) return this.grandpa.right        
        return this.grandpa.left    
    }    
    
    // 修改    
    setData(data) {        
        this.data = data
        return this    
    }    
    
    setLeftChild(node) {        
        this.left = node        
        if (this.left) {            
            this.left.parent = this        
        }     
        return this    
    }    
    
    setRightChild(node) {        
        this.right = node        
        if (this.right) {            
            this.right.parent = this        
        }        
        return this    
    }    
    
    static assign(target, source) {        
        target.setData(source.data)        
        target.setLeftChild(source.left)            
        target.setRightChild(source.right)        
        target.parent = source.parent        
        return target    
    } 
    
    // 中序遍历    
    traverseInorder() {        
        let traverse = []        
        if (this.left) {            
            traverse = [...traverse, ...this.left.traverseInorder()]     
        }    
        
        traverse.push(this.data)        
        
        if (this.right) {            
            traverse = [...traverse, ...this.right.traverseInorder()]         
        }   
                
        return traverse    
    }
}

2-搜索二叉树节点

// ./binary-search-tree-node.js

const BinaryTreeNode = require("./binary-tree-node")

class BinarySearchTreeNode extends BinaryTreeNode {    
    constructor(value, compare) {        
        super(value, compare)    
   }    
   
   get balanceFactor() {        
       return this.leftHeight - this.rightHeight    
   }    
   
   // find inorder precessor of root in left subtree    
   
   findPrecessor() {        
       if (!this.right) return this        
       return this.right.findPrecessor()    
   }    
   
   // 查找    
   find(data) {        
       if (this.compare(this.data, data) === 0) return this        
       if (this.compare(this.data, data) < 0 && this.right) return this.right.find(data)        
       if (this.compare(this.data, data) > 0 && this.left) return this.left.find(data)        
       return null    
   }    
   
   contains(data) {        
       return !!this.find(data)    
   }    
   
   // 新增    
   append(data) {        
       if (Object.is(this.data, null)) {            
           return this.setData(data)        
       }   
       // append to right
       if (this.compare(this.data, data) <= 0) {            
           if (this.right) {                
               return this.right.append(data)            
           }            
           const newNode = new BinarySearchTreeNode(data)            
           this.setRightChild(newNode)            
           return newNode        
       }  
       // append to left
       if (this.left) {            
           return this.left.append(data)        
       }        
       const newNode = new BinarySearchTreeNode(data)        
       this.setLeftChild(newNode)        
       return newNode  
   }    
      
   // 删除    
   remove(data) {        
       const nodeToRemove = this.find(data)        
       if (!nodeToRemove) {            
           throw new Error(`remove error: node with ${data} is not found in the tree`);        
       }    
       
       const { parent } = nodeToRemove  
       
       // nodeToRemove without children        
       if (!nodeToRemove.left && !nodeToRemove.right) {            
           if (parent) {                
               if (this.compare(parent.data, nodeToRemove.data) <= 0) {                    
                   parent.setRightChild(null)                
               } else {                    
                   parent.setLeftChild(null)                
               }                
               return nodeToRemove            
           } else {                
               // for root                
               return nodeToRemove.setData(null)            
           }        
       }   
   
       // 2) nodeToRemove with two children        
       if (nodeToRemove.left && nodeToRemove.right) {            
           const nodeToRemoveCopy = new BinarySearchTreeNode()        
           BinaryTreeNode.assign(nodeToRemoveCopy, nodeToRemove)            
           // find the in order predessor of nodeToRemove            
           const predessor = nodeToRemove.left.findPrecessor()            
           if (predessor === nodeToRemove.left) {                
               nodeToRemove.setData(nodeToRemove.left.data)                
               nodeToRemove.setLeftChild(nodeToRemove.left.left)            
           } else {                
               // remove predessor                
               nodeToRemove.setData(predessor.data)                
               predessor.parent.setRightChild(null)            
           }           
               return nodeToRemoveCopy        
     }  
         
       // 3) nodeToRemove with one child        
     const child = nodeToRemove.left || nodeToRemove.right        
     // If it has parent, just set parent point to child of nodeToRemove        
     if (parent) {            
           if (parent.compare(parent.data, nodeToRemove.data) <= 0) {                
               parent.setRightChild(child)           
           } else {                
               parent.setLeftChild(child)            
           }            
           return nodeToRemove        
      }  
           
      // If no parent, replace nodeToRemove data with it child data        
      const nodeToRemoveCopy = new BinarySearchTreeNode()        
      BinaryTreeNode.assign(nodeToRemoveCopy, nodeToRemove)        
      nodeToRemove.setData(child.data)        
      nodeToRemove.setLeftChild(null)        
      nodeToRemove.setRightChild(null)        
      return nodeToRemoveCopy    
   }
}

3-搜索二叉树

// ./binary-search-tree.js

const BinarySearchTreeNode = require("./binary-search-tree-node")

class BinarySearchTree {    
    constructor(compare) {        
        this.root = new BinarySearchTreeNode(null, compare)        
        this.compare = compare    
    } 
    
    append(data) {        
        return this.root.append(data)    
    }    

    remove(data) {        
        return this.root.remove(data)    
    }    

    find(data) {        
        return this.root.find(data)    
    }    

    contains(data) {        
    return this.root.contains(data)    
    }    

    traverse() {        
        return this.root.traverseInorder()    
    }
}

module.exports = BinarySearchTree

// **************TEST***********************
const list = [-20, 6, 20, 10, -10, 25, 15]
const tree = new BinarySearchTree()
// test appendlist.forEach((v) => { tree.append(v) })

// test traverse
console.log(tree.traverse());

// test find and contains
console.log(tree.contains(10));

// test remove
// 1) without children
console.log(tree.remove(10)); 

// 2) with one child
console.log(tree.remove(15));

// 3) with two children
console.log(tree.remove(20));

// 4) not found
console.log(tree.remove(1));

4-平衡二叉树

// ./avl-tree.js

const BinarySearchTree = require("./binary-search-tree")

class AVLTree extends BinarySearchTree {  

    // right child with right subtree    
    leftRotate(rootNode) {        
        const rightNode = rootNode.right        
        rootNode.setRightChild(rightNode.left) 
        
        if (rootNode.parent) {            
            rightNode.parent = rootNode.parent            
            if (this.compare(rootNode.parent.data, rootNode.data) <= 0) {                
                rightNode.parent.setRightChild(rightNode)            
            } else {                
                rightNode.parent.setLeftChild(rightNode)            
            }        
        } else {            
            this.root = rightNode            
            rightNode.parent = null        
        }    
        
        rightNode.setLeftChild(rootNode)    
    }    
        
    // left child with left subtree,    
    rightRotate(rootNode) {        
        const leftNode = rootNode.left        
        rootNode.setLeftChild(leftNode.right) 
        
        if (rootNode.parent) {            
            leftNode.parent = rootNode.parent            
            if (this.compare(rootNode.data, rootNode.parent.data) <= 0) {                
                leftNode.parent.setRightChild(leftNode)            
            } else {                
                leftNode.parent.setLeftChild(leftNode)            
            }        
        } else {            
            this.root = leftNode            
            leftNode.parent = null        
        }  
        
        leftNode.setRightChild(rootNode)    
    }    
        
    // left child with right subtree    
    leftRightRotate(rootNode) {        
        this.leftRotate(rootNode.left)        
        this.rightRotate(rootNode)    
    }   
    
    // right child with left subtree    
    rightLeftRotate(rootNode) {        
        this.rightRotate(rootNode.right)        
        this.leftRotate(rootNode)    
    }    
    
    checkBalance(node) { 
        // when node is parent of root, just return 
        if(!node) return 
    
        if (Math.abs(node.balanceFactor) > 1) {            
            this.rebalance(node)        
        }        
        if (node.parent) {            
            this.checkBalance(node.parent)        
        }    
    }  
    
    rebalance(node) {       
        // imbalance in left         
        if (node.balanceFactor > 1) {            
            //left subtree             
            if (node.left.balanceFactor > 0) {                
                this.rightRotate(node)            
            } else {                
                // right subtree                
                this.leftRightRotate(node)            
            }        
        } else {            
            //left subtree             
            if (node.right.balanceFactor > 0) {                
                this.rightLeftRotate(node)            
            } else {                
                // right subtree               
                this.leftRotate(node)            
            }        
        }    
    }  
    
    append(value) {        
        const appendedNode = super.append(value)        
        this.checkBalance(appendedNode)    
    }
    
    remove(value) {
        const removedNode = super.remove(value)
        this.checkBalance(removedNode.parent)
 }
        
 module.exports = AVLTree

// **************TEST***********************
const list = [-20, 6, 20, 10, -10, 25, 15]
const tree = new AVLTree()

// test append
list.forEach((v) => { tree.append(v) })

// test traverse
console.log(tree.traverse());

// test remove

// 1) without children
console.log(tree.remove(15)); 

// 2) with one child
console.log(tree.remove(10));

// 3) with two children
console.log(tree.remove(20));

// 4) not found
console.log(tree.remove(1));