JS二叉樹

姜小白發表於2017-11-02

生成一棵二叉樹

二叉樹實現原理

  • 把第一位當做根節點,比根節點小的數放在左子樹上,比根節點大的數放到右子樹上,以此類推。

  • 把下面陣列生成一個二叉樹:let nodes = [8,3,6,4,9,11,2,5,7];

  • 結構如下

  • 生成的二叉樹物件如下

      let tree = {
          key: 8,
          left: {
              key: 3,
              left: {
                  key: 2,
                  left: null,
                  right: null
              },
              right: {
                  key: 6,
                  left: {
                      key: 4,
                      left: null,
                      right: {
                          key: 5,
                          left: null,
                          right: null
                      }
                  },
                  right: {
                      key: 7,
                      left: null,
                      right: null
                  }
              }
          }
          right: {
              key: 9,
              left: null,
              right: {
                  key: 11,
                  left: null,
                  right: null
              }
          }
      }複製程式碼

生成二叉樹實現程式碼

//API:
//insert新增一個子樹,傳值Number
//bulkInsert批量新增子樹,傳值Array
//showTree返回二叉樹物件
class BinaryTree {
    constructor(tree = []) {
        this.root = null;//樹根
        this.Node = key => {
            //生成一個新的子樹
            let _obj = Object.create(null, {});
            _obj.key = key;
            _obj.left = null;
            _obj.right = null;
            return _obj;
        }
        //初始化二叉樹
        if (typeof tree === 'number') {
            this.insert(tree);
        } else if (Array.isArray(tree)) {
            this.bulkInsert(tree)
        } else {
            console.error('請輸入Number型別或者Array型別的引數')
        }
    }
    insert(key) {
        //新增一個新子樹
        let newNode = this.Node(key);
        let _insertNode = (node, newNode) => {
            //判斷新二叉樹的值和原有節點的值
            if (newNode.key < node.key) {
                if (node.left === null) {
                    //判斷左節點是否為空
                    node.left = newNode;
                } else {
                    _insertNode(node.left, newNode)
                }
            } else {
                if (node.right === null) {
                    //判斷右節點是否為空
                    node.right = newNode;
                } else {
                    _insertNode(node.right, newNode)
                }
            }
        }
        if (this.root === null) {
            //如果沒有根節點,那麼把傳入的值當根節點
            this.root = newNode;
        } else {
            //如果有根節點,那麼把傳入的值插到二叉樹上
            _insertNode(this.root, newNode);
        }
    }
    bulkInsert (nodes) {
        nodes.forEach(key => {
            //遍歷陣列,插入子樹
            this.insert(key);
        })
    }
    showTree () {
        //返回二叉樹物件
        return this.root;
    }
}


let nodes = [8,3,6,4,9,11,2,5,7];
let binaryTree = new BinaryTree(nodes);
let tree = binaryTree.showTree();複製程式碼

先序遍歷、中序遍歷、後序遍歷

  • 先序遍歷
    • 根左右
  • 中序遍歷
    • 左根右
  • 後序遍歷
    • 左右根
class BinaryTree {
    ......//生成樹的程式碼
    inOrderTraverse (fn) {
        //中序遍歷,傳入一個回撥函式
        let inOrderTraverseNode = (node, callback) => {
            if (node !== null) {
                inOrderTraverseNode(node.left, callback);
                callback(node.key);
                inOrderTraverseNode(node.right, callback);
            }
        }
        inOrderTraverseNode(this.root, fn)
    }

    preOrderTraverse (fn) {
        //先序遍歷,傳入一個回撥函式
        let preOrderTraverseNode = (node, callback) => {
            if (node !== null) {
                callback(node.key);
                preOrderTraverseNode(node.left, callback);
                preOrderTraverseNode(node.right, callback);
            }
        }
        preOrderTraverseNode(this.root, fn)
    }

    postOrderTraverse (fn) {
        //先序遍歷,傳入一個回撥函式
        let postOrderTraverseNode = (node, callback) => {
            if (node !== null) {
                postOrderTraverseNode(node.left, callback);
                postOrderTraverseNode(node.right, callback);
                callback(node.key);
            }
        }
        postOrderTraverseNode(this.root, fn)
    }
}

let nodes = [8,3,6,4,9,11,2,5,7];
let binaryTree = new BinaryTree(nodes);
let arr1 = [],
    arr2 = [],
    arr3 = [];

binaryTree.inOrderTraverse(key => {
    arr1.push(key);//中序遍歷[2, 3, 4, 5, 6, 7, 8, 9, 11]
})
binaryTree.preOrderTraverse(key => {
    arr2.push(key);//先序遍歷[8, 3, 2, 6, 4, 5, 7, 9, 11]
})
binaryTree.postOrderTraverse(key => {
    arr3.push(key);//後序遍歷[2, 5, 4, 7, 6, 3, 11, 9, 8]
})複製程式碼

二叉樹節點查詢

  • 查詢最大值、最小值
class BinaryTree {
    ......//生成樹的程式碼與三種遍歷程式碼
    min() {
        let node = this.root;
        if (node) {
            //迴圈遍歷左子樹
            while (node && node.left !== null) {
                node = node.left;
            }
            return node.key;
        }
    }

    max() {
        let node = this.root;
        if (node) {
            //迴圈遍歷右子樹
            while (node && node.right !== null) {
                node = node.right;
            }
            return node.key;
        }
    }
}

let nodes = [8,3,6,4,9,11,2,5,7];
let binaryTree = new BinaryTree(nodes);

let min = binaryTree.min();//2
let max = binaryTree.max();//11複製程式碼
  • 查詢二叉樹中節點是否存在
class BinaryTree {
    ......//生成樹的程式碼與三種遍歷程式碼
    search(key) {
        let searchNode = (node, key) => {
            if (node === null) {
                return false;
            }
            if (key < node.key) {
                //當查詢的值小於當前節點的值,則遞迴其左子樹
                return searchNode(node.left, key);
            } else if (key > node.key){
                //當查詢的值大於當前節點的值,則遞迴其右子樹
                return searchNode(node.right, key);
            } else {
                return true;
            }
        }
        return searchNode(this.root, key)
    }
}

let nodes = [8,3,6,4,9,11,2,5,7];
let binaryTree = new BinaryTree(nodes);

let inTree1 = binaryTree.showTree(2);//true
let inTree2 = binaryTree.showTree(1);//false複製程式碼

二叉樹節點刪除

  • 需要判斷這個節點是否還有子樹,有三種情況,無子樹,有單子樹,有雙子樹。
class BinaryTree {
    ......//生成樹的程式碼與三種遍歷程式碼
    remove(key) {
        let findMinNode = (node, key) => {
            //查詢最小子節點
            let node = node || this.root;
            if (node) {
                while (node && node.left !== null) {
                    node = node.left;
                }
                return node;
            }
            return null;
        }
        let removeNode = (node, key) => {
            if (node === null) {
                //如果為null,返回null
                return null
            }

            if (key < node.key) {
                //如果值小於當前節點值,遞迴其左子樹
                node.left = removeNode(node.left, key);
                return node;
            } else if (key > node.key) {
                //如果值大於當前節點值,遞迴其右子樹
                node.right = removeNode(node.right, key);
                return node;
            } else {
                //如果值等於當前節點值,做該節點刪除操作
                if (node.left === null && node.right === null) {
                    //如果該節點沒有左右子樹,直接使其為null
                    node = null;
                    return node;
                }
                if (node.left === null) {
                    //如果該節點只有右子樹,則將該節點指向它的右子樹
                    node = node.right;
                    return node;
                } else if (node.right === null) {
                    //如果該節點只有左子樹,則將該節點指向它的左子樹
                    node = node.left;
                    return node;
                } 

                if (node.left !== null && node.right !== null) {
                    //如果該節點有左右子樹,則把找到它的右子樹的最小子節點,並使該節點的值等於它的右子樹的最小子節點的值,然後刪除它的右子樹的最小子節點
                    let aux = findMinNode(node.right);
                    node.key = aux;
                    node.right = removeNode(node.right, aux.key);
                    return node;        
                }
            }
        }
        this.root = removeNode(this.root, key)
    }
}

let nodes = [8,3,6,4,9,11,2,5,7];
let binaryTree = new BinaryTree(nodes);複製程式碼

JS二叉樹完整程式碼

class BinaryTree {
    constructor(tree = []) {
        this.root = null;//樹根
        this.Node = key => {
            //生成一個新的子樹
            let _obj = Object.create(null, {});
            _obj.key = key;
            _obj.left = null;
            _obj.right = null;
            return _obj;
        }
        //初始化二叉樹
        if (typeof tree === 'number') {
            this.insert(tree);
        } else if (Array.isArray(tree)) {
            this.bulkInsert(tree)
        } else {
            console.error('請輸入Number型別或者Array型別的引數')
        }
    }
    insert(key) {
        //新增一個新子樹
        let newNode = this.Node(key);
        let _insertNode = (node, newNode) => {
            //判斷新二叉樹的值和原有節點的值
            if (newNode.key < node.key) {
                if (node.left === null) {
                    //判斷左節點是否為空
                    node.left = newNode;
                } else {
                    _insertNode(node.left, newNode)
                }
            } else {
                if (node.right === null) {
                    //判斷右節點是否為空
                    node.right = newNode;
                } else {
                    _insertNode(node.right, newNode)
                }
            }
        }
        if (this.root === null) {
            //如果沒有根節點,那麼把傳入的值當根節點
            this.root = newNode;
        } else {
            //如果有根節點,那麼把傳入的值插到二叉樹上
            _insertNode(this.root, newNode);
        }
    }
    bulkInsert (nodes) {
        nodes.forEach(key => {
            //遍歷陣列,插入子樹
            this.insert(key);
        })
    }
    showTree () {
        //返回二叉樹物件
        return this.root;
    }

    inOrderTraverse (fn) {
        let inOrderTraverseNode = (node, callback) => {
            if (node !== null) {
                inOrderTraverseNode(node.left, callback);
                callback(node.key);
                inOrderTraverseNode(node.right, callback);
            }
        }
        inOrderTraverseNode(this.root, fn)
    }

    preOrderTraverse (fn) {
        let preOrderTraverseNode = (node, callback) => {
            if (node !== null) {
                callback(node.key);
                preOrderTraverseNode(node.left, callback);
                preOrderTraverseNode(node.right, callback);
            }
        }
        preOrderTraverseNode(this.root, fn)
    }

    postOrderTraverse (fn) {
        let postOrderTraverseNode = (node, callback) => {
            if (node !== null) {
                postOrderTraverseNode(node.left, callback);
                postOrderTraverseNode(node.right, callback);
                callback(node.key);
            }
        }
        postOrderTraverseNode(this.root, fn)
    }

    min() {
        let node = this.root;
        if (node) {
            while (node && node.left !== null) {
                node = node.left;
            }
            return node.key;
        }
    }

    max() {
        let node = this.root;
        if (node) {
            while (node && node.right !== null) {
                node = node.right;
            }
            return node.key;
        }
    }



    search(key) {
        let searchNode = (node, key) => {
            if (node === null) {
                return false;
            }
            if (key < node.key) {
                return searchNode(node.left, key);
            } else if (key > node.key){
                return searchNode(node.right, key);
            } else {
                return true;
            }
        }
        return searchNode(this.root, key)
    }

    remove(key) {
        let findMinNode = (node, key) => {
            let node = node || this.root;
            if (node) {
                while (node && node.left !== null) {
                    node = node.left;
                }
                return node;
            }
            return null;
        }
        let removeNode = (node, key) => {
            if (node === null) {
                return null
            }

            if (key < node.key) {
                node.left = removeNode(node.left, key);
                return node;
            } else if (key > node.key) {
                node.right = removeNode(node.right, key);
                return node;
            } else {
                if (node.left === null && node.right === null) {
                    node = null;
                    return node;
                }
                if (node.left === null) {
                    node = node.right;
                    return node;
                } else if (node.right === null) {
                    node = node.left;
                    return node;
                } 

                if (node.left !== null && node.right !== null) {
                    let aux = findMinNode(node.right);
                    node.key = aux;
                    node.right = removeNode(node.right, aux.key);
                    return node;        
                }
            }
        }
        this.root = removeNode(this.root, key)
    }
}

let nodes = [8,3,6,4,9,11,2,5,7];
let binaryTree = new BinaryTree(nodes);複製程式碼

相關文章