递归思想

来源:互联网 发布:淘宝大学江西商学院 编辑:程序博客网 时间:2024/06/02 01:15

本文借鉴:http://blog.csdn.net/m13666368773/article/details/7531463

今天说说递归思想,在我们编码时,有的时候递归能够让我们的算法更加通俗易懂,并且代码量也是大大的减少。比如我先前的系列中说到了

关于树的“先序,中序和后序”遍历,那么看看用递归来描叙这个问题是多少的简洁,多么的轻松。

[csharp] view plaincopyprint?
  1. #region 二叉树的先序遍历  
  2.           /// <summary>  
  3.   /// 二叉树的先序遍历  
  4.   /// </summary>  
  5.   /// <typeparam name="T"></typeparam>  
  6.   /// <param name="tree"></param>  
  7.           public void BinTree_DLR<T>(ChainTree<T> tree)  
  8.           {  
  9.               if (tree == null)  
  10.                   return;  
  11.     
  12.               //先输出根元素  
  13.               Console.Write(tree.data + "\t");  
  14.     
  15.               //然后遍历左子树  
  16.               BinTree_DLR(tree.left);  
  17.     
  18.               //最后遍历右子树  
  19.               BinTree_DLR(tree.right);  
  20.           }  
  21.           #endregion  
  22.    
  23.           #region 二叉树的中序遍历  
  24.           /// <summary>  
  25.   /// 二叉树的中序遍历  
  26.   /// </summary>  
  27.   /// <typeparam name="T"></typeparam>  
  28.   /// <param name="tree"></param>  
  29.           public void BinTree_LDR<T>(ChainTree<T> tree)  
  30.           {  
  31.               if (tree == null)  
  32.                   return;  
  33.     
  34.               //优先遍历左子树  
  35.               BinTree_LDR(tree.left);  
  36.     
  37.               //然后输出节点  
  38.               Console.Write(tree.data + "\t");  
  39.     
  40.               //最后遍历右子树  
  41.               BinTree_LDR(tree.right);  
  42.           }  
  43.           #endregion  
  44.    
  45.           #region 二叉树的后序遍历  
  46.           /// <summary>  
  47.   /// 二叉树的后序遍历  
  48.   /// </summary>  
  49.   /// <typeparam name="T"></typeparam>  
  50.   /// <param name="tree"></param>  
  51.           public void BinTree_LRD<T>(ChainTree<T> tree)  
  52.           {  
  53.               if (tree == null)  
  54.                   return;  
  55.     
  56.               //优先遍历左子树  
  57.               BinTree_LRD(tree.left);  
  58.     
  59.               //然后遍历右子树  
  60.               BinTree_LRD(tree.right);  
  61.     
  62.               //最后输出节点元素  
  63.               Console.Write(tree.data + "\t");  
  64.           }  
  65.           #endregion  


 

看看,多么简洁明了。当然递归都是可以改成非递归的,但是就不见得简洁和通俗易懂了。

 

一: 概念

       递归,说白了就是直接或者间接的调用自己的一种算法。它是把求解问题转化为规模较小的子问题,然后通过多次递归一直到可以得出结果

的最小解,然后通过最小解逐层向上返回调用,最终得到整个问题的解。总之递归可以概括为一句话就是:“能进则进,不进则退”。

 

二:三要素

      <1>  递归中每次循环都必须使问题规模有所缩小。

      <2>  递归操作的每两步都是有紧密的联系,如在“递归”的“归操作时”,前一次的输出就是后一次的输入。

      <3>  当子问题的规模足够小时,必须能够直接求出该规模问题的解,其实也就是必须要有结束递归的条件。

 

三: 注意

       <1>  前面也说了,递归必须要有一个递归出口。

       <2>  深层次的递归会涉及到频繁进栈出栈和分配内存空间,所以运行效率比较低,当问题规模较大时,不推荐使用。

       <3>  在递归过程中,每次调用中的参数,方法返回点,局部变量都是存放在堆栈中的,如果当问题规模非常大时,容易造成堆栈溢出。

 

四:  举二个例子

       <1> 相信大家在初中的时候都学过阶乘吧,比如:5!=5*4*3*2*1

        思路:根据上面的阶乘特征很容易我们就可以推导出n!=n*(n-1)*(n-2)....*2*1,

                  那么进一步其实就是: n!=n*(n-1)! ,

                                              (n-1)!=(n-1)*(n-2)!。

                 显然他是满足递归的三要素,当n的规模不大时,我们可以用递归拿下。

[java] view plaincopyprint?
  1. @Test  
  2. public void testJC() {  
  3.     while (true) {  
  4.         // 阶乘问题  
  5.         System.out.println("请输入一个求阶乘的一个数:");  
  6.   
  7.         Scanner scanner = new Scanner(System.in);  
  8.         int num = scanner.nextInt();  
  9.   
  10.         System.out.println("阶乘的结果为:" + fact(num));  
  11.     }  
  12. }  
  13.   
  14. public int fact(int n) {  
  15.     if (n == 1)  
  16.         return 1;  
  17.   
  18.     return n * fact(n - 1);  
  19. }  



 

 

第一次: 输入5的时候能够正确求出。

第二次: 输入10的时候求出来竟然362万之多,可见多恐怖,如果俺们的时间复杂度是n!,那程序也就Game Over了,

第三次:输入100,已经超过了int.MaxValue了,

第四次: 输入10w,蹦出著名了“堆栈溢出”,好家伙,我们知道“递归”在程序中使用“栈”的形式存放的,每一次“递归”中,方法的返回值

          包括函数中的参数都会存放在栈中,C#中每个线程分配的栈空间为1M,所以当N的规模非常大时,就把栈玩爆了。

 

   <2> 在大一时上计算机文化基础的时候我们就接触过”进制转换问题“,比如将”十进制“转化为”二进制“。

       思路:采用除2取余法,取余数为相应二进制数的最低位,然后再用商除以2得到次低位.......直到最后一次相除商为0时得到二进制的最高位,

                比如(100)10=(1100100)2,   仔细分析这个问题,会发现它是满足”递归“的三要素的,

               ① 进制转换中,数据规模会有所缩小。

               ② 当商为0时,就是我们递归的出口。

            所以这个问题我们就可以用递归拿下。

[java] view plaincopyprint?
  1. private String mm = "";  
  2.   
  3. @Test  
  4. public void testConvertToBinary() {  
  5.     int num = 100;  
  6.     convertToBinary(num);  
  7.     System.out.println("十进制数字:"+num+", 转换为二进制为:"+mm);  
  8. }  
  9.   
  10. public void convertToBinary(int num) {  
  11.     int a = num % 2;  
  12.     num = num / 2;  
  13.     if (num > 0) {  
  14.         convertToBinary(num);  
  15.     }  
  16.     mm = mm + a;  
  17. }  





 运行结果:

[html] view plaincopyprint?
  1. 十进制数字:100, 转换为二进制为:1100100  


扩展其他方式:

1、循环实现

[java] view plaincopyprint?
  1. @Test  
  2. public void test3(){  
  3.    int a = 100;  
  4.    String s ="";  
  5.    while(a>0){  
  6.      s = a%2 + s;  
  7.      a /= 2;  
  8.    }  
  9.  System.out.println(s);  
  10. }  


2、 java API实现

[java] view plaincopyprint?
  1. @Test  
  2. public void test2(){  
  3.     int a = 100;  
  4.     System.out.println(Integer.toBinaryString(a));  
  5. }  


 

0 0