1. 程式人生 > 其它 >資料結構篇(7)排序二叉樹及其三種遍歷 程式碼

資料結構篇(7)排序二叉樹及其三種遍歷 程式碼

/**
 * 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())