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

心已入冬 提交于 2020-01-26 18:46:30

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

递归热身

 

一个算法调用自己来完成它的部分工作,在解决某些问题时,一个算法需要调用自身。如果一个算法直接调用自己或间接地调用自己,就称这个算法是递归的(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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!