二叉树的三序遍历
方法主要有三种但我只摸出来两种
-
递归
-
迭代
要明白遍历的本质,本质并不是数字输出的顺序,遍历的本质而是以什么样的顺序去访问节点。
递归
首先就是递归,递归其实感觉没啥好讲的,就是直接暴力递归,遵循树遍历的规则就好了。
一般属于力扣的简单题范畴。
前序遍历
根左右
public void preOrder(TreeNode root, List<Integer> list) { //递归的结束条件 if(root == null) return; //将根节点的值添加到集合中 list.add(root.val); //左递归 preOrder(root.left, list); //右递归 preOrder(root.right, list); }
中序遍历
左根右
public void inOrder(TreeNode root, List<Integer> list) { //递归的结束条件 if(root == null) return; //左递归 inOrder(root.left, list); //将根节点的值添加到集合中 list.add(root.val); //右递归 inOrder(root.right, list); }
后序遍历
左右根
public void postOrder(TreeNode root, List<Integer> list) { //递归结束的条件 if(root == null) return; //左递归 postOrder(root.left, list); //右递归 postOrder(root.right, list); //将根节点的值添加到集合中 list.add(root.val); }
迭代
这个就比较难了,一般属于中等题范畴。
但其实就把递归的底层原理实现了一遍罢了。
递归的底层原理就是栈,当每次递归时将函数压入栈中,当遇到了结束条件返回值然后出栈。
先序遍历
public List<Integer> preorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); if(root == null) return list; //创建一个栈用来模拟递归 Deque<TreeNode> stack = new LinkedList<>(); //开始迭代,判断条件为栈是否为空或者该节点是否为空 //如果栈不为空说明还没遍历完 //如果该节点不为空说明这个节点是可以被压入栈的 while(!stack.isEmpty() || root != null) { //将此节点添加到集合中 //然后左迭代,一直往左迭代,直到左边没节点了 while(root != null) { //将根节点添加到集合中 list.add(root.val); //左迭代 stack.push(root); root = root.left; } //此时左跌倒到头了root==null因此要往后退一个 //此时相当于递归的结束条件if(root == null) return; //本次递归结束所以要出栈 root = stack.pop(); //左迭代完了,该右迭代啦 root = root.right; } return list; }
中序遍历
public List<Integer> inorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); if(root == null) return list; //创建一个栈来模拟递归 Deque<TreeNode> stack = new LinkedList<>(); //开始迭代,判断条件为栈是否为空或者该节点是否为空 //如果栈不为空说明还没遍历完 //如果该节点不为空说明这个节点是可以被压入栈的 while(!stack.isEmpty() || root != null) { //左迭代,一直向左子树迭代,直到没有左子树 while(root != null) { stack.push(root); root = root.left; } //此时root==null因此要往后退一个 //此时相当于递归的结束条件if(root == null) return; //本次递归结束所以要出栈 root = stack.pop(); //将根节点放入集合中 list.add(root.val); //右迭代 root = root.right; } return list; }
后序遍历
后序遍历是迭代中最难理解的,因为根节点放在最后所以遍历时要判断此时的节点是否是最后一次遍历
要实现该功能则要引入一个新的变量pre记录当前节点的前一个节点,如果该节点的前一个节点就是右节点说明右子树已经遍历过了,那么此节点就是第三此被遍历则要将此节点放入集合
public List<Integer> postorderTraversal(TreeNode root) { List<Integer> list = new ArrayList<>(); if(root == null) return list; Deque<TreeNode> stack = new LinkedList<>(); TreeNode pre = null; //开始迭代,判断条件为栈是否为空或者该节点是否为空 //如果栈不为空说明还没遍历完 //如果该节点不为空说明这个节点是可以被压入栈的 while(!stack.isEmpty() || root != null) { //左迭代,一直向左子树迭代,直到没有左子树 while(root != null) { stack.push(root); root = root.left; } root = stack.pop(); //当右节点不是空节点并且右节点并没有被遍历过,那么就可以进行右迭代 if(root.right != null && root.right != pre) { stack.push(root); root = root.right; } else { //此时root节点为最后一次遍历将root放入list中 list.add(root.val); //将上一个节点的记录更新为此节点root pre = root; //好的此时迭代三遍已经结束了root可以消失了 root = null; } } return list; }