递归示例(一):遍历二叉树

最近做项目经常用到递归,刚开始很久没用,不太熟悉,现在研究了下,并写下了学习笔记及开发经验总结。

递归热身

 

一个算法调用自己来完成它的部分工作,在解决某些问题时,一个算法需要调用自身。如果一个算法直接调用自己或间接地调用自己,就称这个算法是递归的(Recursive)。根据调用方式的不同,它分为直接递归(Direct Recursion)和间接递归(Indirect Recursion)。 比如,在收看电视节目时,如果演播室中也有一台电视机播放的是与当前相同的节目,观众就会发现屏幕里的电视套有一层层的电视画面。这种现象类似于直接递归。 

如果把两面镜子面对面摆放,便可从任意一面镜子里看到两面镜子无数个影像,这类似于间接递归。 

一个递归算法必须有两个部分:初始部分(Base Case)和递归部分(Recursion Case)。初始部分只处理可以直接解决而不需要再次递归调用的简单输入。递归部分包含对算法的一次或多次递归调用,每一次的调用参数都在某种程度上比原始调用参数更接近初始情况。 

函数的递归调用可以理解为:通过一系列的自身调用,达到某一终止条件后,再按照调用路线逐步返回。递归是程序设计中强有力的工具,有很多数学函数是以递归来定义的。 

如大家熟悉的阶乘函数,我们可以对n!作如下定义:f(n)= 

1 (n=1)

n*f(n-1)  (n>=2)

 

      一个算法具有的特性之一就是有穷性(Finity):一个算法总是在执行有穷步之后结束,即算法的执行时间是有限的。递归算法当然也是算法,也满足算法的特性,因此递归不可能无限递归下去,总有一个终止条件。对该示例,递归的终止条件是n=1. n=1是,返回1,不在调用自己本身,递归结束。

 

 

 

 
       
         
class Program
{
static void Main( string [] args)
{
long result = function( 20 );
Console.WriteLine(result);
Console.ReadLine();
}

static long function( long n)
{
if (n == 1 ) // 算法终止条件
{
return 1 ;
}

return n * function(n - 1 );
}
}



递归算法通常不是解决问题最有效的计算机程序,因为递归包含函数调用,函数调用需要时空开销。所以,递归比其他替代选择诸如while循环等,所花费的代价更大。但是,递归通常提供了一种能合理有效地解决某些问题的算法。 

递归示例():遍历二叉树

二叉树是一种典型的树形结构,常用到递归算法来遍历。遍历按照根节点的相对顺序可分为前序遍历(DLR)、中序遍历(LDR)、后序遍历(RDL)







对二叉树节点,有数据域存放数据,左孩子和右孩子为引用域存放孩子的引用:

左孩子  LChhild

数据域  data

右孩子 RChild



 


           
             
/// <summary>
/// 二叉树节点
/// </summary>
/// <typeparam name="T"></typeparam>
public class Node < T >
{
private T data; // 数据域
private Node < T > lChild; // 左孩子
private Node < T > rChild; // 右孩子

public Node()
{
data
= default (T);
lChild
= null ;
rChild
= null ;
}

public Node(T data, Node < T > lChild, Node < T > rChild)
{
this .data = data;
this .lChild = lChild;
this .rChild = rChild;
}

public Node(Node < T > lChild, Node < T > rChild)
{
data
= default (T);
this .lChild = lChild;
this .rChild = rChild;
}

public Node(T data)
:
this (data, null , null )
{
this .data = data;
}

/// <summary>
/// 数据域
/// </summary>
public T Data
{
get { return data; }
set { this .data = value; }
}

/// <summary>
/// 左孩子
/// </summary>
public Node < T > LChild
{
get { return lChild; }
set { lChild = value; }
}

/// <summary>
/// 右孩子
/// </summary>
public Node < T > RChild
{
get { return rChild; }
set { rChild = value; }
}

}

 

先假设有以下结构的二叉树:



递归示例(一):遍历二叉树

 

 

 

 

 

 

 

 





先在构造函数中简单构造下对应的数据:



 


                  
                    
public Node < string > A;
public 遍历二叉树()
{
A
= new Node < string > ( " A " );
Node
< string > B = new Node < string > ( " B " );
Node
< string > C = new Node < string > ( " C " );
Node
< string > D = new Node < string > ( " D " );
Node
< string > E = new Node < string > ( " E " );
Node
< string > F = new Node < string > ( " F " );
Node
< string > G = new Node < string > ( " G " );
Node
< string > H = new Node < string > ( " H " );
Node
< string > I = new Node < string > ( " I " );
Node
< string > J = new Node < string > ( " J " );

D.LChild
= H;
D.RChild
= I;

E.LChild
= J;

B.LChild
= D;
B.RChild
= E;

C.LChild
= F;
C.RChild
= G;

A.LChild
= B;
A.RChild
= C;

}



前序遍历:先访问根结点A,然后分别访问左子树和右子树,把BB的子孙看作一个结点处理,CC的子孙看作一个结点处理,访问B时,把B当作根结点处理,B的左子树及左子树的子孙看作一个结点处理……可见,顺序依次是顶点-左孩子-右孩子(DLR),直到结点为叶子(即不包含子结点的结点),即为递归的终止条件。对任意结点,只要结点确定,其左孩子和右孩子就确定,因此递归算法方法参数将结点传入即可。



 

 

 

                    
                      
/// <summary>
/// 前序遍历--DLR
/// </summary>
/// <param name="root"></param>
public void PreOrder(Node < T > root)
{
if (root == null )
{
return ;
}

Console.Write(
" {0} " ,root.Data);
// 当节点无左孩子时,传入参数为null,下次调用即返回,终止
PreOrder(root.LChild);
// 当节点无右孩子时,传入参数为null,下次调用即返回,终止
PreOrder(root.RChild);

}

 

 

同理,中序遍历和后序遍历如下:

 

 
       
         
/// <summary>
/// 中序遍历 LDR
/// </summary>
/// <param name="node"></param>
public void InOrder(Node < T > node)
{
// if (node == null)
// {
// return;
// }
// InOrder(node.LChild);
// Console.Write("{0} ",node.Data);
// InOrder(node.RChild);

// 另外一种写法
if (node.LChild != null )
{
InOrder(node.LChild);
}
Console.Write(
" {0} " , node.Data);
if (node.RChild != null )
{
InOrder(node.RChild);
}
}

/// <summary>
/// 后序遍历--LRD
/// </summary>
/// <param name="node"></param>
public void PostOrder(Node < T > node)
{
if (node == null )
{
return ;
}

PostOrder(node.LChild);
PostOrder(node.RChild);
Console.Write(
" {0} " ,node.Data);
}

/// <summary>
/// 层序遍历
/// </summary>
/// <param name="node"></param>
public void LevelOrder(Node < T > node)
{
if (node == null )
{
return ;
}
Queue
< Node < T >> sq = new Queue < Node < T >> ();
// 根结点入队
sq.Enqueue(node);

while (sq.Count != 0 )
{
Node
< T > tmp = sq.Dequeue(); // 出队
Console.Write( " {0} " ,tmp.Data);

if (tmp.LChild != null )
{
sq.Enqueue(tmp.LChild);
}
if (tmp.RChild != null )
{
sq.Enqueue(tmp.RChild);
}
}
}

 

 

其中,另外一种写法就是在递归前判断下,满足递归条件才调用自己,这也是处理递归终止的一种方法。

       
         
static void Main( string [] args)
{
遍历二叉树
< string > t = new 遍历二叉树 < string > ();
Console.Write(
" 前序遍历: " );
t.PreOrder(t.A);
Console.WriteLine();

Console.Write(
" 中序遍历: " );
t.InOrder(t.A);
Console.WriteLine();

Console.Write(
" 后序遍历: " );
t.PostOrder(t.A);
Console.WriteLine();

Console.Write(
" 层序遍历: " );
t.LevelOrder(t.A);

Console.ReadLine();
}

 

运行结果为

递归示例(一):遍历二叉树

 

 

 

 

 

 



 代码下载: http://files.cnblogs.com/sndnnlfhvk/TViewSource.rar
递归示例(一):遍历二叉树 http://www.cnblogs.com/sndnnlfhvk/archive/2011/03/31/2001015.html
递归示例(二):WinForm之TreeView的应用—绑定区域树  http://www.cnblogs.com/sndnnlfhvk/archive/2011/03/31/2001064.html
递归示例(三):WinForm之TreeView的应用—绑定磁盘目录(一)  http://www.cnblogs.com/sndnnlfhvk/archive/2011/03/31/2001065.html
递归示例(四):WinForm之TreeView的应用—绑定磁盘目录(二)  http://www.cnblogs.com/sndnnlfhvk/archive/2011/03/31/2001072.html

你可能感兴趣的:(二叉树)