3种遍历顺序 二叉树遍历算法,根据根节点相对于左节点、右节点的不同访问顺序,分为前序遍历(Pre-Order Traversal)
、中序遍历(In-Order Traversal)
、后序遍历(Post-Order Traversal)
。
前序遍历:根节点第一个访问:根-左-右;
中序遍历:根节点第二个访问:左-根-右;
后序遍历:根节点最后访问:左-右-根。
示例,假设有这样一个树:
1 2 3 4 5 6 7 1 / \ / \ 2 3 / \ / \ / \ / \ 4 5 6 7
前序遍历:1-2-4-5-3-6-7
中序遍历:4-2-5-1-6-3-7
后序遍历:4-5-2-6-7-3-1
用递归实现的3种遍历是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public class TreeNode { int val ; TreeNode left; TreeNode right; TreeNode(int x ) { val = x; } } public void treeTraversal(TreeNode root ) { if (root == null) { return; } if (root.val is target) { do something } treeTraversal(root .left ) ; treeTraversal(root .right ) ; } public void treeTraversal(TreeNode root ) { if (root == null) { return; } treeTraversal(root .left ) ; if (root.val is target) { do something } treeTraversal(root .right ) ; } public void treeTraversal(TreeNode root ) { if (root == null) { return; } treeTraversal(root .left ) ; treeTraversal(root .right ) ; if (root.val is target) { do something } }
2种遍历策略 树的遍历分为深度优先遍历和宽度优先遍历。
深度优先遍历 深度优先(Depth First Search, DFS)的意思就是,先一条道走到黑,走到不能再走了,再回过头了遍历其他。 下面用一个最简单的例子来说明不同的深度优先遍历。
深度优先遍历大致分为递归实现和非递归实现,递归实现又分为分治法实现和遍历法实现。
用递归实现 分治法 最常见,最好理解的方法,就是分治法。左右叶子节点返回它们自身的深度,取大的那个深度,+1再返回。
1 2 3 4 5 6 7 8 public int maxDepth(TreeNode root ) { if (root == null) { return 0 ; } int left = maxDepth(root .left ) ; int right = maxDepth(root .right ) ; return Math . max(left, right) + 1 ; }
遍历法 遍历法与分治法的本质区别是,分治法有个回归的过程,而遍历法没有。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 int maxDepth;public int maxDepth(TreeNode root ) { maxDepth = 0 ; if (root == null) { return maxDepth; } calculateTreeDepth(root , 1) ; return maxDepth; } public void calculateTreeDepth(TreeNode treeNode , int currentDepth ) { if (treeNode == null) { return; } if (currentDepth > maxDepth) { maxDepth = currentDepth; } calculateTreeDepth(treeNode .left , currentDepth + 1) ; calculateTreeDepth(treeNode .right , currentDepth + 1) ; }
用非递归实现 非递归(即迭代法)实现需要借助栈
这个数据结构,实现方法比递归的麻烦一些,但很重要,一定要理解它的实现原理。 还是以上面的计算树的最大深度为例子(其实更好的做法是宽度优先遍历,这里只是为了展示非递归的实现)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public int maxDepthDfsNonRecursion(TreeNode root) { int maxDepth = 0 ; if (root == null ) { return maxDepth; } Stack <Pair <TreeNode, Integer >> stack = new Stack <>(); stack .push(new Pair <>(root, 1 )); while (!stack .isEmpty()) { Pair <TreeNode, Integer > pair = stack .pop(); TreeNode node = pair .getKey(); int currentDepth = pair .getValue(); if (node.left == null && node.right == null && currentDepth > maxDepth) { maxDepth = currentDepth; } if (node.right != null ) { stack .push(new Pair <>(node.right, currentDepth + 1 )); } if (node.left != null ) { stack .push(new Pair <>(node.left, currentDepth + 1 )); } } return maxDepth; }
宽度优先遍历 宽度优先(Breadth First Search, BFS)的意思就是,优先遍历当前节点的所有子节点,不管子节点还有没有孙子节点。再一层一层向下遍历。
通常需要借助队列
来完成遍历,思路是:记录当前层的数量,开始遍历,将叶子节点放入队列,当遍历的数量等于当前层数量时,说明当前层已经遍历完了;检查队列是否为空,不为空说明还有下层,继续重复刚才的操作;知道全部遍历完成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public int maxDepthBfs (TreeNode root) { int maxDepth = 0 ; if (root == null) { return maxDepth; } Queue<TreeNode> queue = new LinkedList<>(); queue .offer(root); while (!queue .isEmpty()) { int currentLevelSize = queue .size (); int visitedSize = 0 ; while (visitedSize < currentLevelSize) { TreeNode node = queue .poll(); if (node != null) { if (node.left != null) { queue .offer(node.left); } if (node.right != null) { queue .offer(node.right); } } visitedSize++; } maxDepth++; } return maxDepth; }