資料結構篇(7)排序二叉樹及其三種遍歷 程式碼
阿新 • • 發佈:2022-03-31
/** * parent: 雙親節點 * left: 左子節點 * right: 右子節點 */ interface TreeNode { left: TreeNode | null, right: TreeNode | null, data: any, count: number } class TreeNode { constructor(data: any, left: TreeNode | null, right: TreeNode | null) { this.data = data; this.left = left; this.right = right; this.count = 1; } } //二叉排序樹 interface BsTree { root: TreeNode | null //刪除一個節點 _removeNode(node: TreeNode, data: any): void //刪除給定的資料節點 remove(data: any): void //向二叉樹中插入節點 insert(data: any): void //尋找給定資料的節點 find(data: any): TreeNode | null //獲得最小值的節點 getMinNode(node: TreeNode): TreeNode | null //獲得最大值的節點 getMaxNode(node: TreeNode): TreeNode | null display(): void } class BsTree { root: TreeNode | null = null /** * * @param node 要刪除的節點 * @param data 要刪除值 * 遞迴刪除節點 * 待刪除的節點是葉子節點。 * 待刪除的節點沒有左子節點,或者沒有右子節點。 * 待刪除的節點的左右子節點均存在。 * 當待刪除的節點時葉子節點時,這種情況比較簡單,直接將待刪除的節點置空返回即可。 * 當待刪除的節點沒有左子節點時,返回該節點的右孩子節點,並刪除該節點。待刪除節點沒有右節點時類似處理。 * 比較麻煩的是最後一種情況,待刪除的節點的左右子節點均存在時,可以有兩種做法:要麼查詢待刪除節點左子樹上的最大值,要麼查詢其右子樹上的最小值。 * 這裡使用查詢其右子樹上的最小值的方法。在找到待刪除節點的右子樹上的最小值後,建立一個臨時節點,將臨時節點上的值複製到待刪除節點,然後再刪除臨時節點。 */ _removeNode(node: TreeNode, data: any): TreeNode | null { if (node == null) { return null; } if (data == node.data) { if (node.left == null && node.right == null) { return null; } //沒有左節點的節點 if (node.left == null) return node.right; //沒有右節點的節點 if (node.right == null) return node.left; //有兩個節點的節點 /* 做法: 找到待刪除節點的右子樹上的最小值建立一個臨時節點。 將臨時節點上的值複製到待刪除節點,然後再刪除臨時節點 */ // 尋找右子樹上的最小值 let tmpNode = this.getMinNode(node.right); if (tmpNode) { node.data = tmpNode.data; node.right = this._removeNode(node.right, tmpNode.data); return node; } } else if (data < node.data && node.left) { // 待刪除節點在左子樹上 node.left = this._removeNode(node.left, data); return node; } else { // 待刪除節點在右子樹上 if (node.right) { node.right = this._removeNode(node.right, data); return node; } } return null } /** * * @param data 要刪除的節點值 */ //刪除給定的資料節點 remove(data: any): void { if (this.root) { console.log(this._removeNode(this.root, data)); } } /** * 如果在插入時,root節點為空,則直接將新節點賦給root節點即可。 * 如果新的節點值小於當前節點值,說明待插入的位置應在在當前節點的左子樹上,那麼在大於時,就應該在當前節點的右子樹上。進而更新當前節點所指向的節點,直到當前節點為空時,說明找到了正確的插入位置。 */ insert(data: any): void { let newNode = new TreeNode(data, null, null); let parentNode = null; if (this.root === null) { this.root = newNode; } else { let currNode: any = this.root; while (true) { parentNode = currNode; if(newNode.data > currNode.data) { currNode = currNode.right; if(!currNode) { parentNode.right = newNode; break; } } else if(newNode.data < currNode.data) { currNode = currNode.left; if(!currNode) { parentNode.left = newNode; break; } } else if(currNode.data === newNode.data) { currNode.count++; break; } } } } //尋找給定資料的節點 find(data: any): TreeNode | null { if (!this.root) { return null; } let currNode: TreeNode = this.root; while (currNode) { if (data > currNode.data && currNode.right) { currNode = currNode.right; } else if (data < currNode.data && currNode.left) { currNode = currNode.left; } else { return currNode; } } return null; } //獲得最小值的節點 getMinNode(node: TreeNode | null = this.root): TreeNode | null { let currNode = node; while (currNode?.left) { currNode = currNode.left; } return currNode; } //獲得最大值的節點 getMaxNode(node: TreeNode | null = this.root): TreeNode | null { let currNode = node; while (currNode?.right) { currNode = currNode.right; } return currNode; } preOrderRec(node: any = this.root) { let result = ''; if (!(node == null)) { result += `${node.data} `; result += this.preOrderRec(node.left); result += this.preOrderRec(node.right); } return result; } // 前序遍歷非遞迴方法 preOrderNonRec(node:any = this.root) { let stack:Array<any> = []; let result = ''; while(node || stack.length) { if(node) { result += `${node.data} `; stack.push(node); node = node.left; } else { node = stack.pop(); node = node.right; } } return result; } inOrderRec(node:any = this.root) { let result = ''; if(!(node == null)) { result += this.inOrderRec(node.left); result += `${node.data} `; result += this.inOrderRec(node.right); } return result; } //中序遍歷非遞迴演算法 inOrderNonRec(node:any = this.root) { let result = ''; let stack:Array<any> = []; while(node || stack.length) { if(node) { stack.push(node); node = node.left } else { node = stack.pop(); result+=`${node.data} `; node = node.right; } } return result; } //後續遍歷 postOrderRec(node:any = this.root) { let result = ''; if(!(node == null)) { result += this.postOrderRec(node.left); result += this.postOrderRec(node.right); result += `${node.data} ` } return result; } } let myTree = new BsTree(); myTree.insert(20); myTree.insert(13); myTree.insert(7); myTree.insert(9); myTree.insert(15); myTree.insert(14); myTree.insert(42); myTree.insert(22); myTree.insert(21); myTree.insert(24); myTree.insert(57); myTree.remove(9); console.log(myTree.postOrderRec())