树的前中后顺遍历(递归、栈、颜色标记)---Java实现

二叉树的前中后顺遍历(递归、栈)Java实现

前序遍历

力扣144

  • 递归
import java.util.ArrayList;
import java.util.List;

public class PreOrder {

    public List<Integer> preOrder(TreeNode root) {
        List<Integer> res = new ArrayList<>();

        helper(root, res);

        return res;
    }

    public void helper(TreeNode root, List<Integer> res) {
        if (root != null) {
            res.add(root.val);

            if (root.left != null) {
                helper(root.left, res);
            }

            if (root.right != null) {
                helper(root.right, res);
            }

        }
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x;}
}
public List<Integer> stackTravers_preorder(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();

        TreeNode curr = root;
        while (curr != null || !stack.isEmpty()) {
            while (curr != null) {
                res.add(curr.val);
                stack.push(curr);
                curr = curr.left;
            }

            if (!stack.isEmpty()) {
                curr = stack.pop();
                curr = curr.right;

            }
        }

        return res;
    }

中序遍历

力扣94

  • 递归
import java.util.ArrayList;
import java.util.List;

public class PreOrder {

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        helper(root, res);
        return res;
    }

    // recursive
    public void helper(TreeNode root, List<Integer> res) {
        if (root != null) {
            if (root.left != null) {
                helper(root.left, res);
            }

            res.add(root.val);

            if (root.right != null) {
                helper(root.right, res);
            }

        }
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x;}
}
public List<Integer> stackTraversl(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();

        TreeNode curr = root;
        while (curr != null || !stack.isEmpty()) {

            // push the left tree into stack
            while (curr != null) {
                stack.push(curr);
                curr = curr.left;
            }

    		// when reached leaf node
            curr = stack.pop();
            res.add(curr.val);
            curr = curr.right;
        }

        return res;
    }

后续遍历

力扣145

  • 递归
import java.util.ArrayList;
import java.util.List;

public class PostOrder {

    public List<Integer> postOrderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        helper(root, res);
        return res;
    }

    // recursive
    public void helper(TreeNode root, List<Integer> res) {
        if (root != null) {
            if (root.left != null) {
                helper(root.left, res);
            }
            if (root.right != null) {
                helper(root.right, res);
            }
            res.add(root.val);
        }
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x;}
}

算法核心思想:
首先要搞清楚先序、中序、后序的非递归算法共同之处:用栈来保存先前走过的路径,以便可以在访问完子树后,可以利用栈中的信息,回退到当前节点的双亲节点,进行下一步操作。
后序遍历的非递归算法是三种顺序中最复杂的,原因在于,后序遍历是先访问左、右子树,再访问根节点,而在非递归算法中,利用栈回退到时,并不知道是从左子树回退到根节点,还是从右子树回退到根节点,如果从左子树回退到根节点,此时就应该去访问右子树,而如果从右子树回退到根节点,此时就应该访问根节点。所以相比前序和后序,必须得在压栈时添加信息,以便在退栈时可以知道是从左子树返回,还是从右子树返回进而决定下一步的操作。
————————————————
版权声明:本文为CSDN博主「coder__666」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/coder__666/article/details/80349039

public List<Integer> stackTravers_postorder(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        Stack<Integer> stack2 = new Stack<>();      // 辅助栈,用于保存,压入栈的是左子树还是右子树

        TreeNode curr = root;
        int left = 1;  // note left tree
        int right = 2; // note right tree

        while (curr != null || !stack.isEmpty()) {

            while (curr != null) {
                stack.push(curr);
                stack2.push(left);
                curr = curr.left;
            }

            while (!stack.isEmpty() && stack2.peek() == right) {
                // 如果是从左子节点返回父节点,则任务完成,将两个栈的栈顶弹出,记录结果
                stack2.pop();
                res.add(stack.pop().val);
            }

            if (!stack.isEmpty() && stack2.peek() == left) {
                // 如果是从左子树返回,则先将辅助栈弹出栈顶,下一个应该压栈的是右子树
                //
                stack2.pop();
                stack2.push(right);
                curr = stack.peek().right;

            }

        }


        return res;
    }

颜色标记法(通用)

颜色标记法

方题解中介绍了三种方法来完成树的中序遍历,包括:

递归
借助栈的迭代方法
莫里斯遍历
在树的深度优先遍历中(包括前序、中序、后序遍历),递归方法最为直观易懂,但考虑到效率,我们通常不推荐使用递归。

栈迭代方法虽然提高了效率,但其嵌套循环却非常烧脑,不易理解,容易造成“一看就懂,一写就废”的窘况。而且对于不同的遍历顺序(前序、中序、后序),循环结构差异很大,更增加了记忆负担。

因此,我在这里介绍一种“颜色标记法”(瞎起的名字……),兼具栈迭代方法的高效,又像递归方法一样简洁易懂,更重要的是,这种方法对于前序、中序、后序遍历,能够写出完全一致的代码。

其核心思想如下:

使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
如果遇到的节点为灰色,则将节点的值输出。

public class ColorMethod {

    // 定义含有颜色信息的树节点
    class ColorNode{
        TreeNode node;
        int color;

        public ColorNode(TreeNode node, int color) {
            this.node = node;
            this.color = color;
        }
    }
    
    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x) { val = x;}
	}

    private final int white = 0;
    private final int gray = 1;


    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();

        if (root == null) {
            return res;
        }

        Stack<ColorNode> stack = new Stack<>();
        stack.push(new ColorNode(root, white));

        while (!stack.isEmpty()) {
            ColorNode curr = stack.pop();

            // 如果此节点没有访问过
            if (curr.color == white) {
                if (curr.node.right != null) {
                    stack.push(new ColorNode(curr.node.right, white));
                }
                // 置为灰色
                stack.push(new ColorNode(curr.node, gray));
                if (curr.node.left != null) {
                    stack.push(new ColorNode(curr.node.left, white));
                }
            } else {
                res.add(curr.node.val);
            }

        }
        return res;
    }


}

N叉树的前序遍历

力扣 589

递归

/**
 * N叉树的前序遍历
 */
public class Recursive {

    // 使用递归来前序遍历
    public List<Integer> preorder(Node root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        helper(root, res);
        return res;
    }

    private void helper(Node curr, List<Integer> res) {
        if (curr != null) {
            res.add(curr.val);
            for (Node temp : curr.children) {
                helper(temp, res);
            }
        }
    }

}

class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
}

栈-迭代

/**
 * 使用栈,采用迭代的方式,来实现多叉树的遍历
 */
public class StackIterator {

    public List<Integer> preorder(Node root) {
//        List<Node> stack = new LinkedList<>();
        Stack<Node> stack = new Stack<>();
        List<Integer> res = new LinkedList<>();

        if (root == null) {
            return res;
        }

        stack.add(root);

        while (!stack.isEmpty()) {
            // 前序遍历,因此是root left tight
            // 入栈的顺序要反过来
            Node curr = stack.pop();
            res.add(curr.val);
            if (curr.children != null) {
                Collections.reverse(curr.children);
                for (Node temp : curr.children) {
                    stack.push(temp);
                }
            }
        }

        return res;
    }

颜色标记法

public class ColorMethod {

    private final int white = 0;
    private final int gray = 1;

    public List<Integer> preorder(Node root) {
        List<Integer> res = new LinkedList<>();

        if (root == null) {
            return res;
        }

        Stack<ColorNode> stack = new Stack<>();
        stack.push(new ColorNode(root, white));

        while (!stack.isEmpty()) {
            ColorNode curr = stack.pop();

            if (curr.color == white) {
                // 以相反的顺序压栈
                if (curr.node.children != null) {
                    Collections.reverse(curr.node.children);
                    for (Node temp : curr.node.children) {
                        stack.push(new ColorNode(temp, white));
                    }
                    stack.push(new ColorNode(curr.node, gray));
                }
            } else {
                res.add(curr.node.val);
            }
        }

        return res;
    }


}


class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
}

// 定义含有颜色信息的树节点
class ColorNode{
    Node node;
    int color;

    public ColorNode(Node node, int color) {
        this.node = node;
        this.color = color;
    }
}

参考链接

CSDN

力扣 中序遍历 题解

力扣题解-颜色标记法

posted @ 2020-06-26 23:53  唐啊唐囧囧  阅读(502)  评论(0编辑  收藏  举报