这几天在复习算法,又遇到树的遍历。
树的遍历递归比较简单;循环就比较麻烦了,看了别人的写法老是想不通是怎么做到的。
今天静下心来想想,得出了自己的一套“白痴”的方案。
--本篇先讲简单的---递归遍历。
一、预备知识
树的遍历方式:
1.先序遍历:对于一个节点,1读本身,2读左子,3读右子。
2.中序遍历:对于一个节点,1读左子,2读本身,3读右子。
3.后序遍历:对于一个节点,1读左子,2读右子,3读本身。
怎么记:
不管怎么样,左子肯定在右子前面;先序中序后序,指的是本身节点放在第一个、第二个还是第三个。
那位置不就确定下来了吗?
二、先想好用递归怎么做
想要用递归来做树的遍历,那这个时候眼里就只有一个节点。
那好,现在手里拿着一个树节点,我会怎么做(左中右为例)?
1.把事情扔给左节点去做。
2.左边的处理完了,轮到自己了,读取本身节点。
3.把事情扔给左节点去做。
三、动手
先把环境搭一下:
3个类,一个TreeNode实体类,一个遍历的工具类,一个测试专用类
1.TreeNode类
package com.aii.algorithm;
public class TreeNode {
int value;
TreeNode left;
TreeNode right;
@Override
public String toString() {
return "TreeNode [value=" + value + "]";
}
public TreeNode() {
}
public TreeNode(int value) {
this.value = value;
}
}
2.遍历的工具类
package com.aii.algorithm;
import java.util.Queue;
import java.util.Stack;
/**
* 完全二叉树的先序,后序,中序 - 递归/循环遍历
* */
public class TreeConverter {
// //========递归
// 递归遍历,先序遍历
public static void readTreeByRecursionFirst(TreeNode root,
Queue<TreeNode> to) {
if (to == null) {
exception();
}
if (root == null) {
return;
}
to.offer(root);
readTreeByRecursionFirst(root.left, to);
readTreeByRecursionFirst(root.right, to);
}
// 递归遍历,中序遍历
public static void readTreeByRecursionMiddle(TreeNode root,
Queue<TreeNode> to) {
if (to == null) {
exception();
}
if (root == null) {
return;
}
readTreeByRecursionMiddle(root.left, to);
to.offer(root);
readTreeByRecursionMiddle(root.right, to);
}
// 递归遍历,后序遍历
public static void readTreeByRecursionEnd(TreeNode root, Queue<TreeNode> to) {
if (to == null) {
exception();
}
if (root == null) {
return;
}
readTreeByRecursionEnd(root.left, to);
readTreeByRecursionEnd(root.right, to);
to.offer(root);
}
// 对于错误的参数,直接抛异常,让调用者知道
private static void exception() {
throw new RuntimeException("queue can not be null ");
}
}
3.测试类
package com.aii.algorithm;
import java.util.LinkedList;
import java.util.Queue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TreeConverterTest {
private TreeNode root = new TreeNode(10);
private Queue<TreeNode> container = new LinkedList<TreeNode>();
// ----10
// ---↓--↓
// ---6--14
// --↓-↓-↓-↓
// --4-8-12-16
@Before
public void init() {
TreeNode t7 = new TreeNode(16);
TreeNode t6 = new TreeNode(12);
TreeNode t5 = new TreeNode(8);
TreeNode t4 = new TreeNode(4);
TreeNode t3 = new TreeNode(14);
TreeNode t2 = new TreeNode(6);
root.left = t2;
root.right = t3;
t2.left = t4;
t2.right = t5;
t3.left = t6;
t3.right = t7;
}
// /递归
@Test
public void testReadTreeByRecursionFirst() {
TreeConverter.readTreeByRecursionFirst(root, container);
}
@Test
public void testReadTreeByRecursionMiddle() {
TreeConverter.readTreeByRecursionMiddle(root, container);
}
@Test
public void testReadTreeByRecursionEnd() {
TreeConverter.readTreeByRecursionEnd(root, container);
}
@After
public void print() {
System.out.println("result:" + container);
}
}
正确的结果是:
先序:10-6-4-8-14-12-16
中序:4-6-8-10-12-14-16
后序:4-8-6-12-16-14-10
四、总结
递归遍历非常简单,只要搞清楚当前节点要做什么,把其他的任务扔给其他的节点,调用自己本身这个方法就行了。