二叉樹系列(前序遍歷-中序遍歷-後序遍歷)

二叉搜索樹的創建

定義Node爲節點類,Tree爲二叉樹,設置方法insert用於增加樹上的結點,根據節點和樹上已有結點的權值大小確定插入位置。
二叉樹

/* 結點 */
function Node(val, left, right) {
    this.val = val;
    this.left = null;
    this.right = null;
}
/* 樹 */
function Tree() {
    this.root = null;
}
/* 結點插入二叉樹 */
Tree.prototype.insert = function (val) {
    /* 創建新節點 */
    let newNode = new Node(val, null, null);
    /* 樹根爲空直接賦值 */
    if (!this.root) {
        this.root = newNode;
    } else {
        let cur = this.root;
        /* 找到位置才能退出循環 */
        while (true) {
            if (newNode.val > cur.val) {
                /* 右子樹爲空,直接將新節點作爲右孩子 */
                if (!cur.right) {
                    cur.right = newNode;
                    break;
                } else {
                    cur = cur.right;
                }
            }
            if (newNode.val < cur.val) {
                if (!cur.left) {
                    cur.left = newNode;
                    break;
                } else {
                    cur = cur.left;
                }
            }
        }
    }
}
let tree = new Tree();
let arr = [4, 6, 5, 2, 3, 1];
arr.forEach(v => tree.insert(v));

遞歸方式實現三種遍歷

遞歸調用,選擇對應時機輸出即可,缺點是太耗性能.

var orderTraversal = function (root) {
    let res = [];
    function order(rt) {
        if (rt) {
            res.push(rt.val);//前序
            order(rt.left);
            // res.push(rt.val);//中序
            order(rt.right);
            // res.push(rt.val);//後序
        }
    }
    order(root);
    return res;
}

前序遍歷

實現: 彈出棧頂元素node,將棧頂元素的右結點right左結點left放到棧裏面,然後取出棧頂元素left作爲父結點重複該操作,只有left下的所有結點全部輸出完,right纔會進行輸出,從而實現根->左->右的輸出順序。
leetcode:https://leetcode-cn.com/problems/binary-tree-preorder-traversal/

每次循環stack變化:
[4]--------->4出棧,62進棧
[6,2]------->2出棧,31進棧
[6,3,1]----->1出棧
[6,3]------->3出棧
[6]--------->6出棧,5進棧
[5]--------->5出棧
/* 根->左->右 */
var preorderTraversal = function (root) {
    if (!root) return [];
    let res = [], stack = [root];
    /* 設置棧存序列 */
    while (stack.length) {
        let node = stack.pop();/* 棧頂元素 */
        res.push(node.val);
        /* 後進先出,左邊的在後面進入,確保每次先彈出左邊結點 */
        if (node.right) stack.push(node.right);
        if (node.left) stack.push(node.left);
    }
    return res;
};
console.log(preorderTraversal(tree.root));//[ 4, 2, 1, 3, 6, 5 ]

中序遍歷

實現: 用棧來存儲結點,只有結點左子樹全部輸出完該結點纔可能輸出,查到沒有左孩子的結點node進行輸出,然後再到node的右子樹進行同樣的操作.
leetcode:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/

/* 左->根->右 */
var inorderTraversal = function (root) {
    if (!root) return [];
    let res = [], stack = [], parent = root;
    while (stack.length || parent) {
        /* 一直往左下找,直到最左下方結點 */
        while (parent) {
            stack.push(parent);
            parent = parent.left;
        }
        let node = stack.pop();//彈出最左下方結點
        res.push(node.val);
        parent=node.right;//完成(左->根),對該結點右下方的結點進行遍歷
    }
    return res;
};
console.log(inorderTraversal(tree.root));//[ 1, 2, 3, 4, 5, 6 ]

後序遍歷

實現: 在中序遍歷的基礎上添加一個判斷,只有右子樹全部輸出才能輸出當前結點。
leetcode:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/

/* 左->右->根 */
var postorderTraversal = function (root) {
    if (!root) return [];
    let set = new Set();//記錄結點是否被訪問過
    let stack = [], res = [], parent = root;
    while (stack.length || parent) {
        while (parent) {
            stack.push(parent);
            parent = parent.left;
        }
        let node = stack[stack.length - 1];/* 棧頂元素 */
        /* 對還未輸出的右子樹進行遍歷輸出 */
        if (node.right && !set.has(node.right)) {
            set.add(node.right);//標記node.right下的子樹已經輸出
            parent = node.right;
        } else {
            res.push(node.val);
            stack.pop();
        }
    }
    return res;
}
console.log(postorderTraversal(tree.root));//[ 1, 3, 2, 5, 6, 4 ]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章