public class TreeNode { int val; TreeNode left; TreeNode right; TreeNode() { } TreeNode(int val) { this.val = val; } TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } }
1,先根遍历-递归
【思路】先根遍历,即第一次遇到的结点就开始打印。一直遍历左子树,直到为空,然后开始递归右子树,直到为空。
【过程】先将1进入方法,2,3执行方法,2先执行,在2中将4,5执行方法,直到发现4无子树,然后轮到5执行方法。直到发现5没有子树,此时2方法结束,轮到3执行方法。
class Solution { List a = new ArrayList(); public List<Integer> preorderTraversal(TreeNode root) { if (root != null) { a.add(root.val); preorderTraversal(root.left); //按先根次序遍历p的左子树,递归调用,参数为左孩子 preorderTraversal(root.right); //按先根次序遍历p的右子树,递归调用,参数为右孩子 } return a; } }
LeetCode589:给定一个 n 叉树的根节点
root
,返回 其节点值的 前序遍历 。class Solution { List a = new ArrayList(); public List<Integer> preorder(Node root) { if (root != null) { a.add(root.val); for (int i=0;i<root.children.size();i++){ preorder(root.children.get(i)); } } return a; } }
2,先根遍历-非递归
【思路】还是先根遍历,遇到结点就压入栈中,每当发现某个结点无子树,将结点设置为从栈中弹出元素。如果不为空就打印。
【过程】先将1压入栈中,然后让结点=1.left,1的左子树=2,发现2不为空压入栈中,继续。。。当发现4无子树时,从栈中弹出2,使得p=2,right=5,此时栈中只有1,等发现5没有子树时,p弹栈=1。
class Solution { List a = new ArrayList(); Stack<TreeNode> stack = new Stack(); public List<Integer> preorderTraversal(TreeNode root) { while (root != null || !stack.isEmpty()) { if (root != null) { a.add(root.val); stack.push(root); root = root.left; } else { root = stack.pop(); root = root.right; } } return a; } }
3,中根遍历-递归
【思路】开始从跟结点遍历,当第二次遍历到该结点时打印。如何区分是第二次了,在递归中只需要在左右子树中间打印即可。因为但遍历完左子树时,回到父母结点时为第二次遍历到父母结点,第一次为通过父母接到遍历到孩子结点时。
【过程】从1开始进入遍历方法,发现1不为空,将2,3进入遍历方法。由于2先开始,此时3一直在等待2的结束。2发现4.5不是空,将4,5进入遍历方法,此时没有打印2.当4结束时在通过2进入5之前打印此时结点,即为中根遍历。
class Solution { List a = new ArrayList(); public List<Integer> inorderTraversal(TreeNode root) { if (root != null) { inorderTraversal(root.left); a.add(root.val); inorderTraversal(root.right); } return a; } }
4,中根遍历-非递归
【思路】参考先根遍历中的非递归方法。先根遍历是在第一次遇到非空的时候打印。中根稍有区别,在第一次非空时入栈,不打印,当发现子树为空时,弹栈时回到空子树的父母结点时,打印,此时为第二次访问,即中根遍历。
【过程】1开始进入while循环,发现1.left=2不为空入栈,此时不要打印,然后4,进入循环,入栈,发现4左子树为空,此时弹栈,弹出4,立即打印,达到中根遍历。其余相似。
class Solution { List a = new ArrayList(); Stack<TreeNode> stack = new Stack(); public List<Integer> inorderTraversal(TreeNode root) { while (root != null || !stack.isEmpty()) { if (root != null) { stack.push(root); root = root.left; } else { root = stack.pop(); a.add(root.val); root = root.right; } } return a; } }
5,后根遍历-递归
【思路】现遍历子树,等左右子树都遍历完后,在打印结点。跟之前的类比一下很容易想到。
【过程】1进入递归,发现2,3非空,然后2进入递归,3等待,2又发现4,5非空,此时4进入递归,5等待,然后4发现左右子树都为空,此时不再进行递归,打印4结点的值。
class Solution { List a = new ArrayList(); public List<Integer> postorderTraversal(TreeNode root) { if (root != null) { postorderTraversal(root.left); postorderTraversal(root.right); a.add(root.val); } return a; } }
6,后根遍历-非递归
【思路】由于需要确定当前结点被访问过两次,此时需要额外一个结点来储存之前访问的结点。符合打印条件的就一共两种情况,一是无子结点,直接打印。二是当前访问的结点其右子树已被访问,或者右子树不存子,左子树被访问过。
!!!一定要注意,先将右子树入栈,再将左子树入栈,因为访问时是现访问左子树,再访问右子树。
class Solution { List a = new ArrayList(); Stack<TreeNode> stack = new Stack<>(); public List<Integer> postorderTraversal(TreeNode root) { if (root==null){ return new ArrayList<>(); } TreeNode cur, pre = null; //pre为额外结点,记录前一个结点,用于打印 stack.push(root); while (!stack.isEmpty()) { cur = stack.peek(); if ((cur.left == null && cur.right == null) || (pre != null && (cur.left == pre || cur.right == pre))) { //判断是否为上述条件决定是否打印 TreeNode temp = stack.pop(); a.add(temp.val); pre = temp; } else { if (cur.right != null) { //右结点入栈 stack.push(cur.right); } if (cur.left != null) { stack.push(cur.left); } } } return a; } }