算法之递归

       什么是递归?递归是一种算法思想。从字面上看,递归包含两层含义,传递和回归。现实中有很多问题,只是传递而不用回归。比如说,军训时,每天的一项训练:”报数”。报数从头到尾,依次传递,到最后一个人停止,这时候军官就知道了总体人数是多少,有没有少人。假如,队伍中有人想知道自己是几号,怎么办呢?问下旁边的人,如果旁边的人说自己是“N”号,那么自己是“N+1”号。如果旁边的人不知道自己的号,那么他会继续问他旁边的人,以此类推,知道某个人知道自己是多少号,然后把这个号往回传回传其实就是报数。用一段程序表示:

        public static int GetPeopleCount(int n)
        {
if (n<1) return 0;
if (n == 1) return 1; return GetPeopleCount(n - 1) + 1; }

       有人问了,你这还用得着递归吗?直接循环搞定。我说此时用循环意义变了,那军官从第一个人数到最后一个人,不就是很累吗?递归(报数)效率多高啊。军官自己数的话,相当于每次指针移动。在现实当中不可取,原因不光是累,还有一个致命的原因,就是大家都是迷彩服,数数肯定花眼了,难免出错。

       好了,递归可以用来数数,我们的循环当然可以数数,而且循环效率肯定高。放到计算机中,这只是不同的算法而已。

要算一个数的阶乘,怎么算呢?

        public static int GetNumber(int n)
        {
            if (n < 1) return 0;
            if (n == 1)
                return 1;

            return GetNumber(n - 1) * n;
        }

和上面的数数多么相似啊,只是递归的时候函数变了而已。为了程序的正确性,小测一下,求5的阶乘:

有人说,循环也可以啊,效率也高,那我们改为循环:

       public static int GetNumber(int n)
        {
            int result = 1;

            for (int i = 1; i <= n; i++)
            {
                result = result * i;
            }
            return result;
        }

的确根据阶乘的定义,可以改为循环。

      有一个很经典的问题,那就是 斐(fei)波那契数列(兔子总数) 1,1,2,3,5,8,13..这个数列的特点就是从第三个数开始,每一项都是前两项之和。那这个数列跟兔子又有什么联系呢,原来还有这么个问题:

 有一对兔子,从出生后第三个月起每个月都生一对兔子,小兔子长到第三个月后又生一对兔子,假如兔子都不死,每个月兔子对数为多少? 
第一个月:1对; 
第二个月:1对; 
第三个月:2对; 
第四个月:3对: 
第五个月:5对: 
第六个月:8对;

请问第五个月,为什么是5对?那是因为自己生了一对,在第三个月生下的一对兔子,在第五个月开始生了一对兔子,所以加起来就增加了2对兔子,因此是5,请问第20月的时候,一共有多少兔子?要让人算,恐怕得费点事情了,好在我们发现这个兔子数的规律就是斐(fei)波那契数列。

程序编写为:

       public static int GetNumber(int n)
        {
            if (n < 1) return 0;

            if (n == 1 || n == 2) return 1;

            return GetNumber(n - 2) + GetNumber(n - 1);
        }

第20个月兔子总数:

这么多啊,果然非人力所为,那怎么知道这个方法正确呢?测试下,第6个月:

这段程序依然可以改为循环,就不用写了,为什么我一直把递归和循环比较呢?那是因为我曾经面试的时候,有个面试官问我循环和递归有什么不同,递归在什么时候用,我当时一脸懵逼,心里嘀咕,该用递归时自然用递归。他给的结论是:能用递归的,就能用循环。

果真是这样的吗?

1、给定一个文件夹,找出下面所有的文件

2、遍历二叉树

你给我来个循环试试。这个我就确实用循环写不出来,为什么呢?因为之前的例子,都是数学问题,有明显的规律可寻,但是遍历文件和二叉树没有这样的规律,而且我无法确定循环多少次,什么时候才能循环结束。也许有某些高人能写出来。

附:递归遍历文件

 1          /// <summary>  
 2         /// 获取目录path下所有子文件名  
 3         /// </summary>  
 4         public static List<string> getAllFiles(String path)
 5         {
 6             List<string> fileNames = new List<string>();
 7             if (System.IO.Directory.Exists(path))
 8             {
 9                 //所有子文件名  
10                 string[] files = System.IO.Directory.GetFiles(path);
11                 foreach (string file in files)
12                 {
13                     fileNames.Add(file);
14                 }
15                 //所有子目录名  
16                 string[] Dirs = System.IO.Directory.GetDirectories(path);
17                 foreach (string dir in Dirs)
18                 {
19                     var tmp = getAllFiles(dir);  //子目录下所有子文件名  
20                     if (tmp.Count>0)
21                     {
22                         fileNames.AddRange(tmp);
23                     }
24                 }
25             }
26             return fileNames;
27         }

 

posted @ 2017-12-06 12:35  micDavid  阅读(244)  评论(0编辑  收藏  举报