Нерекурсивная реализация обхода двоичного дерева до и после заказа (JavaScript)

алгоритм

Очень легко использовать рекурсию для реализации обхода двоичных деревьев до среднего порядка. В этой статье представлен метод нерекурсивной реализации обхода до среднего порядка. Основная идея состоит в том, чтобы использовать стек для моделировать рекурсивный процесс реализации посредством итерации.

В следующей реализации root представляет собой корневой узел двоичного дерева.Каждый узел имеет два указателя, левый и правый, которые указывают на левое и правое поддеревья текущего узла соответственно.Атрибут val представляет значение текущего узла.

Обход предзаказа (preorderTraversal)

const preorderTraversal = function(root) {
    const stack = [], res = []
    root && stack.push(root)
    // 使用一个栈stack,每次首先输出栈顶元素,也就是当前二叉树根节点,之后依次输出二叉树的左孩子和右孩子
    while(stack.length > 0) {
        let cur = stack.pop()
        res.push(cur.val)
        // 先入栈的元素后输出,所以先入栈当前节点右孩子,再入栈左孩子
        cur.right && stack.push(cur.right)
        cur.left && stack.push(cur.left)
    }
    return res
};

Неупорядоченный обход (inorderTraversal)

первый метод

const inorderTraversal = function(root) {
    const res = [], stack = []
    while(root || stack.length) {
        // 中序遍历,首先迭代左孩子,左孩子依次入栈
        if(root.left) {
            stack.push(root)
            root = root.left
        // 如果左孩子为空了,输出节点,去右孩子中迭代,
        } else if(root.right) {
            res.push(root.val)
            root = root.right
        // 如果左右孩子都为空了,输出当前节点,栈顶元素出栈,也就是回退到上一层,此时置空节点左孩子,防止while循环重复进入
        } else if(!root.left && !root.right) {
            res.push(root.val)
            root = stack.pop()
            root && (root.left = null)
        }
    }
    return res
};

Второй способ (первая оптимизация)

В предыдущем методе условное суждениеroot.left,root.right, На самом деле мы можем рассматривать только текущий узел node, поэтому нам нужно только судить о том, существует ли узел, упростить код

 const inorderTraversal = function(root) {
    const res = [], stack = []
    let node = root;
    while (stack.length > 0 || node !== null) {
        // 这里用当前节点node是否存在,简化代码,
        if (node) {
            stack.push(node);
            node = node.left
        } else {
            node = stack.pop();
            res.push(node.val);
            node = node.right;
        }
    }
    return res;
};

Постзаказ обход (postorderTraversal)

первый метод

// 1, 先依次遍历左孩子, 在栈中依次记录,当左孩子为空时,遍历到叶子节点 //跳回上一层节点, 为防止while循环重复进入,将上一层左孩子置为空
// 2, 接着遍历右孩子, 在栈中依次记录值,当右孩子为空时, 遍历到叶子节点
// 跳回上一层节点, 为防止while循环重复进入,将上一层右孩子置为空
const postorderTraversal = function(root) {
    let res = [], stack = []
    while (root || stack.length) {
        if (root.left) {
            stack.push(root)
            root = root.left
        } else if (root.right) {
            stack.push(root)
            root = root.right
        } else {
            res.push(root.val)
            root = stack.pop()
            if (root && root.left) root.left = null
            else if (root && root.right) root.right = null
        }
    }
    return res
};

Второй метод (мышление в обратном порядке)

Оглядываясь назад на код обхода предварительного заказа, на самом деле обход после заказа и обход до заказа представляют собой процесс в обратном порядке.

// 结果数组中依次进入的是节点的左孩子,右孩子,节点本身,注意使用的是
// unshift,与前序遍历push不同,每次数组头部添加元素,实际上就是前序 遍历的逆序过程
const postorderTraversal = function(root) {
    const res = [], stack = []
    while (root || stack.length) {
        res.unshift(root.val)
        root.left && stack.push(root.left)
        root.right && stack.push(root.right)
        root = stack.pop()
    }
    return res
};

Третий метод (еще один способ записи мышления в обратном порядке)

// 和前序遍历区别在于,结果数组res中入栈顺序是当前节点,右孩子,左孩子,最后
// 使用js数组reverse方法反转(逆序),使得输出顺序变为左孩子,右孩子,当前节点,实现后序遍历
const postorderTraversal = function(root) {
    let stack = [], res = []
    root && stack.push(root)
    while(stack.length > 0) {
        let cur = stack.pop()
        res.push(cur.val)
        cur.left && stack.push(cur.left)
        cur.right && stack.push(cur.right)
    }
    return res.reverse()
};

В этой статье подробно рассказывается о нерекурсивной реализации обхода бинарных деревьев до, в середине и после порядка.Ядро заключается в использовании стека для имитации рекурсивного процесса итеративным способом.