PS:本文系转载文章,阅读原文可读性会更好,文章末尾有原文链接
目录
1、二叉树的遍历
1、1 前序遍历
1、2 中序遍历
1、3 后续遍历
1、二叉树的遍历
1、1 前序遍历
从Java版的数据结构和算法(三)这篇文章中,我们学到了二叉树的常用术语和二叉树的概念,这里我们说一下二叉树的遍历,我们可以使用前序遍历、中序遍历和后序遍历这3种方式对二叉树进行遍历;我们先说说前序遍历,前序遍历的输出顺序是:父节点 -> 左子树 -> 右子树,注意:叶子节点也可以看作一棵子树。
好,我们列举一下前序遍历的步骤:
(1)创建一棵二叉树。
(2)先输出当前节点(初始化的时候是根节点)。
(3)如果左子树不为空,则递归继续前序遍历。
(4)如果右子树不为空,则递归继续前序遍历。
有的读者可能会说,看了前序遍历的步骤,我还是懵啊,没关系,我们举个例子,先画出一棵二叉树,如图1所示:
图片
好,我们用前序遍历说一下图1的思路;
(1)首先1节点是根节点,我们应当优先输出1节点。
(2)然后遍历1节点的左子树2,发现2节点是4节点和5节点的父节点,所以把2节点输出来。
(3)遍历2节点的左子树4,发现4节点是8节点的父节点,所以把4节点输出来。
(4)这时候4节点没有左子树,只有右子树,然后去遍历4节点的右子树8,8节点作为叶子节点直接输出来。
(5)到了这里,2节点的左子树遍历完了,然后去遍历2节点的右子树5,5节点作为9节点的父节点先输出来。
(6)5节点没有左子树,直接去遍历右子树9,发现9节点是叶子节点直接输出来。
(7)到了这里根节点1的左子树遍历完了,然后去遍历右子树3,3节点6节点和7节点的父节点,所以优先输出3节点。
(8)然后又去遍历3节点的左子树6,6节点作为10节点和11节点的父节点,直接输出6节点。
(9)遍历6节点的左子树10,发现10节点是叶子节点直接输出来。
(10)到了这里,6节点的左子树已经遍历完了,然后去遍历6节点的右子树11,发现11节点是叶子节点直接输出来。
(11)到了这里,3节点的左子树已经遍历完了,接着去遍历3节点的右子树7,发现7节点是叶子节点直接输出来。
最后用前序遍历图1的二叉树顺序为:1 2 4 8 5 9 3 6 10 11 7;到了这里,二叉树的前序遍历的规则应该知道了吧?好了,我们现在用代码实现一把创建图1的二叉树并进行前序遍历。
(1)节点类 Node :
public class Node {
private int value;
private Node left;
private Node right;
public Node(int value) {
super();
this.value = value;
}
public void setLeft(Node left) {
this.left = left;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node [value=" + value + "]";
}
public void preOrder() {
//输出当前节点(如果当前节点有子节点,那么它就是父节点,否则就是叶子节点)
System.out.println(this);
//递归向左子树进行前序遍历
if (left != null) {
left.preOrder();
}
//递归向右子树进行前序遍历
if (right != null) {
right.preOrder();
}
}
}
(2)测试类 BinaryTreeTest :
public class BinaryTreeTest {
private Node rootNode;
public static void main(String[] args) {
BinaryTreeTest test = new BinaryTreeTest();
test.createBinaryTree();
test.preOrder();
}
private void createBinaryTree() {
Node[] nodes = new Node[11];
for (int i = 1; i <= nodes.length; i++) {
nodes[i-1] = new Node(i);
}
rootNode = nodes[0];//1节点为根节点
rootNode.setLeft(nodes[1]);//1节点的左子节点是2
rootNode.setRight(nodes[2]);//1节点的右子节点是3
nodes[1].setLeft(nodes[3]);//2节点的左子节点是4
nodes[1].setRight(nodes[4]);//2节点的右子节点是5
nodes[3].setRight(nodes[7]);//4节点的右子节点是8
nodes[4].setRight(nodes[8]);//5节点的右子节点是9
nodes[2].setLeft(nodes[5]);//3节点的左子节点是6
nodes[2].setRight(nodes[6]);//3节点的右子节点是7
nodes[5].setLeft(nodes[9]);//6节点的左子节点是10
nodes[5].setRight(nodes[10]);//6节点的右子节点是11
}
private void preOrder() {
if (rootNode != null) {
System.out.println("二叉树的前序遍历~~");
rootNode.preOrder();
} else {
System.out.println("这是一棵空树");
}
}
}
程序运行结果如下所示:
图片
程序输出的结果跟我们上面分析图1的前序遍历输出顺序是一模一样的。
1、2 中序遍历
中序遍历的输出顺序是:左子树 -> 父节点 -> 右子树,下面我们列举一下中序遍历的步骤:
(1)创建一棵二叉树。
(2)如果当前节点的左子节点不为空,那么则递归中序遍历。
(3)输出当前节点。
(4)如果当前节点的右子节点不为空,那么则递归中序遍历。
首先中序遍历最优先遍历的是左子树对不对,好,下面也举个例子,我们用中序遍历同样说一下图1的思路;
(1)一开始找到根节点1,发现根节点右左右子树,先往下递归左子树进行中序遍历,也就是去遍历左子树2。
(2)发现节点2也有左右子树,也先往下递归左子树进行中序遍历,也就是去遍历右子树4。
(3)发现4节点没有左子树,只有右子树,所以这里先输出4节点,然后去遍历4节点的右子树8。
(4)发现8节点是叶子节点,直接输出8节点。
(5)到了这里2节点的左子树已经遍历完了,然后将2节点直接输出,这时去遍历2节点的右子树5。
(6)发现5节点没有左子树,只有右子树,所以这里就先输出5节点,然后5节点的右子树9,发现9节点是叶子节点,就直接输出9节点。
(7)到了这里,1节点的已经遍历完了,然后将1节点直接输出,这时候去遍历1节点的右子树3。
(8)发现3节点有左右子树,然后先去遍历3节点的左子树6,这时候发现6节点也有左右子树。
(9)先去遍历6节点的左子树10,发现10节点是叶子节点,直接输出10节点。
(10)到了这一步,6节点的左子树已经遍历完了,然后将6节点直接输出,这时候去遍历6节点的右子树11。
(11)发现11节点是叶子节点,直接将11节点输出来。
(12)到了这一步,3节点的左子树已经遍历完了,然后将3节点直接输出来,这时候去遍历3节点的右子树7。
(13)发现7节点是叶子节点,直接将7节点输出来。
最后用中序遍历图1的二叉树顺序为:4 8 2 5 9 1 10 6 11 3 7;到了这里,二叉树的中序遍历的规则我们已经摸清楚了,我们也用代码实现一把创建图1的二叉树并进行中序遍历。
(1)节点类 Node2 :
public class Node2 {
private int value;
private Node2 left;
private Node2 right;
public Node2(int value) {
super();
this.value = value;
}
public void setLeft(Node2 left) {
this.left = left;
}
public void setRight(Node2 right) {
this.right = right;
}
@Override
public String toString() {
return "Node2 [value=" + value + "]";
}
public void midOrder() {
//递归向左子树进行前序遍历
if (left != null) {
left.midOrder();
}
//输出当前节点
System.out.println(this);
//递归向右子树进行前序遍历
if (right != null) {
right.midOrder();
}
}
}
(2)测试类 BinaryTreeTest2 :
public class BinaryTreeTest2 {
private Node2 rootNode;
public static void main(String[] args) {
BinaryTreeTest2 test = new BinaryTreeTest2();
test.createBinaryTree();
test.midOrder();
}
//创建图1的一棵二叉树
private void createBinaryTree() {
Node2[] nodes = new Node2[11];
for (int i = 1; i <= nodes.length; i++) {
nodes[i-1] = new Node2(i);
}
rootNode = nodes[0];//1节点为根节点
rootNode.setLeft(nodes[1]);//1节点的左子节点是2
rootNode.setRight(nodes[2]);//1节点的右子节点是3
nodes[1].setLeft(nodes[3]);//2节点的左子节点是4
nodes[1].setRight(nodes[4]);//2节点的右子节点是5
nodes[3].setRight(nodes[7]);//4节点的右子节点是8
nodes[4].setRight(nodes[8]);//5节点的右子节点是9
nodes[2].setLeft(nodes[5]);//3节点的左子节点是6
nodes[2].setRight(nodes[6]);//3节点的右子节点是7
nodes[5].setLeft(nodes[9]);//6节点的左子节点是10
nodes[5].setRight(nodes[10]);//6节点的右子节点是11
}
//中序遍历
private void midOrder() {
if (rootNode != null) {
System.out.println("二叉树的中序遍历~~");
rootNode.midOrder();
} else {
System.out.println("这是一棵空树");
}
}
}
程序运行结果如下所示:
图片
程序输出的结果跟我们上面分析图1的中序遍历输出顺序也是一模一样的。
1、3 后序遍历
后序遍历的输出顺序是:左子树 -> 右子树 -> 父节点,下面我们列举一下后序遍历的步骤:
(1)创建一棵二叉树。
(2)如果当前节点的左子节点不为空,那么则递归后序遍历。
(3)如果当前节点的右子节点不为空,那么则递归后序遍历。
(4)输出当前节点。
后序遍历最优先遍历的是左子树,其次是右子树,最后是父节点,好,下面也举个例子,我们用后序遍历同样说一下图1的思路;
(1)一开始找到根节点1,发现根节点1有左右2棵子树,先优先去遍历根节点1的左子树2。
(2)发现2节点也有左右2棵子树,也优先去遍历2节点的左子树4。
(3)这时候发现4节点没有左子树,有右子树,于是先去遍历4节点的右子树8。
(4)发现8节点是叶子节点,直接将8节点输出来。
(5)这时候4节点的右子树遍历完了,然后将4节点直接输出来。
(6)到了这里,2节点的左子树已经遍历完了,于是这就去遍历2节点的右子树5。
(7)发现5节点没有左子树,只有右子树,于是先去遍历5节点的右子树9。
(8)发现9节点是叶子节点,直接将9节点输出来。
(9)这时候5节点的右子树已经遍历完了,这就将5节点直接输出来。
(10)也这时候2节点的右子树已经遍历完了,也将2节点直接输出来。
(11)到了这里,根节点1的左子树已经遍历完了,这就去遍历根节点1的右子树3。
(12)发现3节点有左右2棵子树,优先遍历3节点的左子树6。
(13)发现6节点也有左右2棵子树,也优先遍历6节点的左子树10。
(14)发现10节点是叶子节点,直接将10节点输出来。
(15)这时候6节点的左子树遍历完了,于是去遍历6节点的右子树11。
(16)发现11节点是叶子节点,直接将11节点输出来。
(17)到了这里6节点的右子树已经遍历完了,这就将6节点直接输出来。
(18)也到了这里3节点的左子树已经遍历完了,于是去遍历3节点的右子树7。
(19)发现7节点是叶子节点,直接将7节点输出来。
(20)这时候3节点的右子树已经遍历完了,这就将3节点直接输出来。
(21)到了这里根节点1的右子树也已经遍历完了,这就将根节点1输出来。
最后用后序遍历图1的二叉树顺序为:8 4 9 5 2 10 11 6 7 3 1;到了这里,二叉树的后序遍历的规则我们也搞清楚了,我们用代码实现一把图1的后序遍历。
(1)节点类 Node3 :
public class Node3 {
private int value;
private Node3 left;
private Node3 right;
public Node3(int value) {
super();
this.value = value;
}
public void setLeft(Node3 left) {
this.left = left;
}
public void setRight(Node3 right) {
this.right = right;
}
@Override
public String toString() {
return "Node3 [value=" + value + "]";
}
public void postOrder() {
//递归向左子树进行前序遍历
if (left != null) {
left.postOrder();
}
//递归向右子树进行前序遍历
if (right != null) {
right.postOrder();
}
//输出当前节点
System.out.println(this);
}
}
(3)测试类 BinaryTreeTest3 :
public class BinaryTreeTest3 {
private Node3 rootNode;
public static void main(String[] args) {
BinaryTreeTest3 test = new BinaryTreeTest3();
test.createBinaryTree();
test.postOrder();
}
//创建图1的一棵二叉树
private void createBinaryTree() {
Node3[] nodes = new Node3[11];
for (int i = 1; i <= nodes.length; i++) {
nodes[i-1] = new Node3(i);
}
rootNode = nodes[0];//1节点为根节点
rootNode.setLeft(nodes[1]);//1节点的左子节点是2
rootNode.setRight(nodes[2]);//1节点的右子节点是3
nodes[1].setLeft(nodes[3]);//2节点的左子节点是4
nodes[1].setRight(nodes[4]);//2节点的右子节点是5
nodes[3].setRight(nodes[7]);//4节点的右子节点是8
nodes[4].setRight(nodes[8]);//5节点的右子节点是9
nodes[2].setLeft(nodes[5]);//3节点的左子节点是6
nodes[2].setRight(nodes[6]);//3节点的右子节点是7
nodes[5].setLeft(nodes[9]);//6节点的左子节点是10
nodes[5].setRight(nodes[10]);//6节点的右子节点是11
}
//后序遍历
private void postOrder() {
if (rootNode != null) {
System.out.println("二叉树的后序遍历~~");
rootNode.postOrder();
} else {
System.out.println("这是一棵空树");
}
}
}
程序运行结果如下所示:
图片
程序输出的结果跟我们上面分析图1的后序遍历输出顺序也是一模一样的。
小结:如何判断它是前序遍历还是中序遍历或者后续遍历,其实很简单,是有规律的,我们可认为主角是父节点,如果是优先输出父节点,那便是前序遍历;如果优先输出左子树,再输出父节点,那便是中序遍历;如果是最后才输出父节点,那便是后续遍历。