1 二叉树的定义
二叉树顾名思义:每一个父亲节点都至多含有两个子节点的树结构叫做二叉树。
二叉树程序定义可以设置为如下所示:
class TreeNode{
private int data;
private TreeNode left;
private TreeNode right;
}
有了树节点的定义方式,我们就可以对整个树进行查询等操作。以下图二叉树为例讲解层次遍历和递归遍历方法。
在二叉树的结构中由于每一个节点都保存了本节点的数据信息(private int data)以及左右孩子(TreeNode left, right;)的位置信息,
因此我们可以完美的使用递归函数来依次访问树中的每一个节点。
递归函数的3要素:函数、边界条件和递推公式,递归函数形式有3个方面:函数名、返回值和参数列表。
1. 边界条件是结束递归循环的条件
2. 递推公式为函数体内部产生循环的主要部分,在树结构的访问中用来递归调用自身,完成整个树结构的遍历
public static void recurTreeFront ( TreeNode root) {
if ( root == null )
return ;
System . out. print ( root. getData ( ) + " " ) ;
recurTreeFront ( root. getLeft ( ) ) ;
recurTreeFront ( root. getRight ( ) ) ;
}
public static void recurTreeMiddle ( TreeNode root) {
if ( root == null )
return ;
recurTreeMiddle ( root. getLeft ( ) ) ;
System . out. print ( root. getData ( ) + " " ) ;
recurTreeMiddle ( root. getRig public static void recurTreeBehind ( TreeNode root) {
if ( root == null )
return ;
recurTreeBehind ( root. getLeft ( ) ) ;
recurTreeBehind ( root. getRight ( ) ) ;
System . out. printf ( root. getData ( ) + " " ) ; ;
}
在上述代码过程中,可以发现如下几个特点:
1. 在不需要返回树节点元素的时候,我们可以直接将函数定义为void,此时的输出就利用System.out.print(某节点存储的数据)来表示
2. 前中后序遍历算法的次序区别在输出,System.out.println(root.getData())上,如果在某个节点上先输出,在递归调用左右节点则是前序遍历算法
为什么会出现序号2所示的情况?递归函数和循环类似,要分析每一层递归调用或者循环体需要执行的步骤。
递归在一层调用(也就是访问某个节点)时,以root根节点为例,如果是前序遍历,此时进入函数体,先会检查是否为空,如果为空返回到上一层函数体,否则就会向下执行。
此时由于是中序遍历算法,先打印输出当前节点存储的数据data,然后在递归调用左右子树。
二叉树的层次遍历算法
1. 顺序输出每一层的元素
层次遍历算法属于树结构的广度优先算法,前中后序遍历算法属于深度优先算法。
因此在此处我们需要设计另外一种遍历算法区别与深度优先算法。
由于树节点的定义(class TreeNode),我们只知道当前节点和值和该节点的左右两个子节点的位置信息。
此时我们需要先访问父节点才能访问子节点,整体的访问顺序是从根节点出发向下访问。
根据此,我们应该如何对树结构进行层次遍历?
**解决方案**
我们可以想到队列这个数据结构的特点是FIFO,即先进先出,我们可以通过将root节点先压入队列,然后顺序的将其左右子节点依次压入队列。
最后将数据依次出队,得到的结果就是按照每一层的顺序依次输出的。
代码实现
public static LinkedList < TreeNode > simpleLevelOrder ( TreeNode root) {
if ( root == null )
return new LinkedList < TreeNode > ( ) ;
Queue < TreeNode > queue = new LinkedList < > ( ) ;
LinkedList < TreeNode > list = new LinkedList < > ( ) ;
queue. add ( root) ;
while ( ! ( queue. isEmpty ( ) ) ) {
if ( queue. peek ( ) . getLeft ( ) != null )
queue. add ( queue. peek ( ) . getLeft ( ) ) ;
if ( queue. peek ( ) . getRight ( ) != null )
queue. add ( queue. peek ( ) . getRight ( ) ) ;
list. add ( queue. poll ( ) ) ;
}
return list;
}
此时含有一个小问题,利用此函数输出的数据其是一个列表,如何将每一层的数据保存的单独的列表中呢?
此问题的核心是如何得知每一层的节点数量。
我们可以在函数中定义size整形变量。如果树不为空的时候size初始值为1!
以根节点为例,上述代码中,root节点压入到queue以后会执行3个操作:
1. 将左孩子节点压入到队列中
2. 将右孩子节点压入到队列中
3. 将root根节点poll出队列,用返回列表list接收。
在poll出去以后此时queue队列内只剩下了2个root的子节点,也就相应的变成了第二层的数据!
此时size的值就应该由于root的poll出去从1变成0,然后由于队列中两个子节点的数据由第一层的0变成第二层的2.。
同理,当size 减到0的时候,也就是第二层数据全部poll出去的时候,由于两个节点分别调用了压入左右子节点,所以queue的数据就变成了第3层的数据!(如上图所示)
代码实现
public static LinkedList < LinkedList < TreeNode > > simpleLevelLayer ( TreeNode root) {
if ( root == null )
return new LinkedList < > ( ) ;
Queue < TreeNode > queue = new LinkedList < > ( ) ;
queue. add ( root) ;
LinkedList < LinkedList < TreeNode > > list = new LinkedList < > ( ) ;
LinkedList < TreeNode > layer = new LinkedList < > ( ) ;
int size = 1 ;
while ( ! ( queue. isEmpty ( ) ) ) {
if ( queue. peek ( ) . getLeft ( ) != null )
queue. add ( queue. peek ( ) . getLeft ( ) ) ;
if ( queue. peek ( ) . getRight ( ) != null )
queue. add ( ( queue. peek ( ) . getRight ( ) ) ) ;
layer. add ( queue. poll ( ) ) ;
if ( -- size == 0 ) {
list. add ( layer) ;
layer = new LinkedList < > ( ) ;
size = queue. size ( ) ;
}
}
return list;
}
整体代码实现
import java. util. LinkedList ;
import java. util. Queue ;
public class TreeTest {
public static void main ( String [ ] args) {
TreeNode node3 = new TreeNode ( 3 ) ;
TreeNode node9 = new TreeNode ( 9 ) ;
TreeNode node20 = new TreeNode ( 20 ) ;
TreeNode node8 = new TreeNode ( 8 ) ;
TreeNode node13 = new TreeNode ( 13 ) ;
TreeNode node15 = new TreeNode ( 15 ) ;
TreeNode node17 = new TreeNode ( 17 ) ;
node3. setLeft ( node9) ;
node3. setRight ( node20) ;
node9. setLeft ( node8) ;
node9. setRight ( node13) ;
node20. setLeft ( node15) ;
node20. setRight ( node17) ;
LinkedList < TreeNode > list = simpleLevelOrder ( node3) ;
for ( TreeNode node : list) {
System . out. print ( node. getData ( ) + " " ) ;
}
System . out. println ( ) ;
LinkedList < LinkedList < TreeNode > > listLayer = simpleLevelLayer ( node3) ;
for ( LinkedList < TreeNode > list1 : listLayer) {
System . out. print ( "[" ) ;
for ( TreeNode node : list1) {
System . out. print ( node. getData ( ) + " " ) ;
}
System . out. print ( "]," ) ;
System . out. println ( ) ;
}
}
public static LinkedList < TreeNode > simpleLevelOrder ( TreeNode root) {
if ( root == null )
return new LinkedList < TreeNode > ( ) ;
Queue < TreeNode > queue = new LinkedList < > ( ) ;
LinkedList < TreeNode > list = new LinkedList < > ( ) ;
queue. add ( root) ;
while ( ! ( queue. isEmpty ( ) ) ) {
if ( queue. peek ( ) . getLeft ( ) != null )
queue. add ( queue. peek ( ) . getLeft ( ) ) ;
if ( queue. peek ( ) . getRight ( ) != null )
queue. add ( queue. peek ( ) . getRight ( ) ) ;
list. add ( queue. poll ( ) ) ;
}
return list;
}
public static void recurTreeFront ( TreeNode root) {
if ( root == null )
return ;
System . out. print ( root. getData ( ) + " " ) ;
recurTreeFront ( root. getLeft ( ) ) ;
recurTreeFront ( root. getRight ( ) ) ;
}
public static void recurTreeMiddle ( TreeNode root) {
if ( root == null )
return ;
recurTreeMiddle ( root. getLeft ( ) ) ;
System . out. print ( root. getData ( ) + " " ) ;
recurTreeMiddle ( root. getRight ( ) ) ;
}
public static void recurTreeBehind ( TreeNode root) {
if ( root == null )
return ;
recurTreeBehind ( root. getLeft ( ) ) ;
recurTreeBehind ( root. getRight ( ) ) ;
System . out. printf ( root. getData ( ) + " " ) ; ;
}
public static LinkedList < LinkedList < TreeNode > > simpleLevelLayer ( TreeNode root) {
if ( root == null )
return new LinkedList < > ( ) ;
Queue < TreeNode > queue = new LinkedList < > ( ) ;
queue. add ( root) ;
LinkedList < LinkedList < TreeNode > > list = new LinkedList < > ( ) ;
LinkedList < TreeNode > layer = new LinkedList < > ( ) ;
int size = 1 ;
while ( ! ( queue. isEmpty ( ) ) ) {
if ( queue. peek ( ) . getLeft ( ) != null )
queue. add ( queue. peek ( ) . getLeft ( ) ) ;
if ( queue. peek ( ) . getRight ( ) != null )
queue. add ( ( queue. peek ( ) . getRight ( ) ) ) ;
layer. add ( queue. poll ( ) ) ;
if ( -- size == 0 ) {
list. add ( layer) ;
layer = new LinkedList < > ( ) ;
size = queue. size ( ) ;
}
}
return list;
}
}
class TreeNode {
private int data;
private TreeNode left;
private TreeNode right;
public TreeNode ( int data) {
this . data = data;
this . right = null ;
this . left = null ;
}
public int getData ( ) {
return data;
}
public void setData ( int data) {
this . data = data;
}
public TreeNode getLeft ( ) {
return left;
}
public void setLeft ( TreeNode left) {
this . left = left;
}
public TreeNode getRight ( ) {
return right;
}
public void setRight ( TreeNode right) {
this . right = right;
}
}
由于还没学会树的创建,因此在主函数中用了大篇幅定义树结构,如有错误和改进的地方,还望众位大佬批评指正。