二叉搜索樹最麻煩的地方就是刪除結點了,具體步驟如圖。
function BinarySearchTree() { // 定義BST實例的屬性 this.root = null; this.count = 0; // 定義構造結點的函數 function Node(key) { this.key = key; this.left = null; this.right = null; } // 插入 BinarySearchTree.prototype.insert = function (key) { var newNode = new Node(key); if (this.root == null) { this.root = newNode; } else { insertNode(this.root, newNode); } this.count++; // 遞歸插入 function insertNode(node, newNode) { // 向左遞歸 if (node.key > newNode.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); } } } } // 前序遍歷 BinarySearchTree.prototype.preOrderTraverse = function () { var str = ""; pre(this.root) // 遞歸 function pre(node) { if (node == null) { return; } else { str += node.key + " " // 根 pre(node.left); // 左 pre(node.right); // 右 } } console.log(str); } // 中序遍歷 BinarySearchTree.prototype.midOrderTraverse = function () { var str = ""; mid(this.root) // 遞歸 function mid(node) { if (node == null) { return; } else { mid(node.left); // 左 str += node.key + " " // 根 mid(node.right); // 右 } } console.log(str); } // 后序遍歷 BinarySearchTree.prototype.postOrderTraverse = function () { var str = ""; post(this.root) // 遞歸 function post(node) { if (node == null) { return; } else { post(node.left); // 左 post(node.right); // 右 str += node.key + " " // 根 } } console.log(str); } // 最小值 BinarySearchTree.prototype.getMin = function () { return left(this.root); // 利用二叉搜索樹的特性,進行遞歸查找左結點即為最小值 function left(node) { var key; if (node == null) { return key; } else if(node.left = null) { key = left(node.left); } else { key = node.key; } return key; } } // 最大值 BinarySearchTree.prototype.getMax = function () { return right(this.root); // 利用二叉搜索樹的特性,進行遞歸查找右結點即為最大值 function right(node) { var key; if(node == null) { } else if(node.right != null) { key = right(node.right); } else { key = node.key; } return key; } } // 根據key查找結點, 返回node,找不到則返回undefined; BinarySearchTree.prototype.search = function(key) { return search(this.root,key); // 利用二叉搜索樹的特性,進行遞歸查找 function search(node,key) { var result ; if(node === null) { } else if(node.key == key){ result = node; } else { // 向左查找 if(key < node.key ) { result = search(node.left,key); } // 向右查找 else { result = search(node.right,key); } } return result; } } BinarySearchTree.prototype.reMove = function(key) { //第一步:找到要刪除的結點,其中要保留父節點,當前結點,以及標記該結點是父結點的左子還是右子結點。 var parentNode = null; //保留父節點 var current = this.root; //當前結點 var isLeftChild = true; //標記刪除結點是父結點的左子還是右子結點 while(current.key!=key && current != null) { parentNode = current; //保留父節點 //判斷往左 還是往右 if(current.key > key) { current = current.left; isLeftChild = true; } else { current = current.right; isLeftChild = false; } } // 說明未找到。 if(current == null) return false; console.log("當前結點:"+current.key) //第二步:根據找到的結點 進行不同情況的刪除。 // 1 葉節點的情況,我們只需要把葉節點設置成null即可 if(current.left == null && current.right == null) { console.log("刪除葉結點") // 1.1該節點為根結點 if(current == this.root) { this.root = null; } // 1.2該節點不為根結點 else{ isLeftChild ? parentNode.left=null:parentNode.right=null; } } // 2 節點且只有一個子節點的情況 我們只需要把父節點指向子節點即可 // 2.1只有左子結點 else if (current.left != null && current.right == null) { console.log("刪除只有1個子結點的結點") //判斷是否為為根結點 if(current == this.root) { this.root = current.left; } else { isLeftChild ? parentNode.left = current.left : parentNode.right = current.left; } } // 2.2只有右子結點 else if(current.left == null && current.right != null) { console.log("刪除只有1個子結點的結點") //判斷是否為為根結點 if(current == this.root) { this.root = current.right; } else { isLeftChild ? parentNode.left = current.right : parentNode.right = current.right; } } // 3 有2個子節點的情況 我們要根據替換的規律 既是在左子樹中找最大的值(前驅) 或者 在右子樹中找最小的值(后繼) 然后與該結點替換。再將給結點設置為null else { console.log("刪除有2個子結點的結點") // 3.2.1定義前驅結點,前驅結點的父結點 var frontNode = current.left; // 前驅結點 var frontParentNode = current; // 前驅結點的父結點 // 3.2.2循環找前驅結點,前驅結點的父結點 while(frontNode.right!=null) { frontParentNode = frontNode; frontNode = frontNode.right; } console.log("前驅結點"+frontNode.key,"前驅結點的父結點"+frontParentNode.key); // 3.2.3開始刪除結點 // 3.2.3.1因為直接找的是前驅結點,所以前驅結點的右孩子直接指向刪除結點的右孩子即可。 frontNode.right = current.right; // 3.2.3.2判斷前驅結點是刪除結點的子代還是隔代。 // 如果是隔代 在考慮兩種情況下把刪除結點的左子樹賦值給前驅結點。 if(frontParentNode != current ) { //第一種情況,如果前驅結點為葉子結點,那么前驅結點的左孩子指向刪除結點的左子樹,前驅結點的父節點右子樹指向空,避免變成圓形鏈。 if(frontNode.left == null) { frontNode.left = current.left; frontParentNode.right = null; } //第二種情況,如果前驅結點不為葉子結點,根據二叉搜素樹的特性,它必然有左孩子。那么前驅結點的左孩子的左孩子指向刪除結點的左子樹,避免變成圓形鏈。 else { frontNode.left.left = current.left; frontParentNode.right = null; } } // 3.3 判斷刪除結點是否為根結點,如果是,直接把刪除結點的父結點指向刪除結點,同時根節點要重新指向前驅結點。 if(current == this.root) { parentNode = current; this.root = frontNode; } // 3.4刪除結點 isLeftChild ? parentNode.left = frontNode : parentNode.right = frontNode; } } } var bst = new BinarySearchTree(); bst.insert(11); bst.preOrderTraverse(); bst.midOrderTraverse(); bst.postOrderTraverse(); bst.reMove(11) bst.preOrderTraverse();