五、如何递,怎样归?

     很多人看完递归的原理之后会有这种感觉,喔,这个原理我懂了,然后再找一道其余的题目看一看能不能写的出来,突然发现,我勒个去,还是不会。其实这种现象很普遍,所以如果你是这种的,也没有什么好沮丧的,我敢保证你能看的懂递归的执行过程,基本上已经比30%的人要强了。所以我觉得,我写一写我对递归思维的理解好了。递归这个词我的理解应该是传递和回归,如何把自身的状态传递下去和如何回归到一个结果上是递归问题的基本思维方式。

      所谓如何传递,我觉得思维的难点是如何抽象出数学模型,如果是斐波那契数列那种有明确公式的话,很简单,直接按照公式该怎么操作怎么操作,难得是只有叙述性语言的,比如这种题目:有一段楼梯n个阶梯,你可以选择一次上一个阶梯,也可以选择一次上两个阶梯,请问走到顶部一共有多少种走法?看似很高深吧?其实这就是斐波那契数列的一个变体而已。这种描述性的题目如果要抽象出数学模型,我觉得最好的办法就先列举几个试试,先看看有什么规律没有,然后再猜想,再证明。你先看看你上2层楼梯有几种方法,2层楼梯要么是1次性上去,要么分成两步,一次性上一步,于是就是F(2)=2,如果只有一层和没有呢,那明显只有一种走法(一次上一层和不走),也就是F(0)=1,F(1)=1,下面,你要上第三层,你的办法要么是从第二层上一层到第三层,要么是在第一层上两层到第三层,要么一层一层的走上去,这样F(3)=3,看起来还是没有什么规律,接着往下来,现在要上第四层了,那么让我们换一种思维方式,怎样到第四层呢?要么你在第三层到第四层,要么从第二层到第四层,为什么不说从第一层到第四层呢?因为如果你把这个当作一种情况的话,你会发现在第一层的时候,无论下一步你怎样做都会回到上面两种情况之中。所以到第四层的作法就是F(3)+F(2),因为你到了第三层或者到了第二层(如果你在第二层选择上一层那么就会和在第三层的走法重合),后面的走法就确定了,不同的是前面的走法,也就是F(4)=5,现在让我们增加点难度,如果你要到第n层,那么应该说最后一步你有可能是从第n-1层走两层上来的,也有可能是从第n层走两层上来的,也就是说到第n层的走法决定于你怎么走到第n-1层和第n层的,所以这个走法应该是F(n)=F(n-1)+F(n-2)。

还有一种不知道如何传递是不知道怎样将递归算法转换成程序,你知道怎样用语言描述出递归,但是就是不知道怎样用程序描写出来,所以最好的方式是找一段递归的程序,然后看他每一次递归的输出。

关于如何归,就是要找到递归中止的条件,比如斐波那契数列那个,n=0就是数列的中止条件,别小看这个中止条件,如果不能找出这个中止条件或者定义错误的话,后果就是无限的递归,导致程序堆栈的崩溃,最终整个程序就很快的崩溃掉了。

我们从一个简单的开始,使用递归算法求最大公约数,利用辗转相除法,简单的说就是对于两个数m和n,利用公式gcd(m,n)=gcd(n,m%n)=

gcd(m%n,m%n%n),直到后面的余数为0为止,这是个有数学公式的比较明显的递归模式,所以按照这个数学公式的逻辑,这个递归算法的回归的话n==0的时候,所以这个算法很容易写出来。

最大公约数

代码相当的简单,思路要很清晰。那么,再来看一个二分搜索的好了,二分搜索是在已经排序好的数列里面寻找目标数,比如{1,2,...,10},这种,如果是寻找2,那么先求出这一组数的中值5,2比5小,从而转到0-5这个部分,其中值是2,然后就找到了。这种搜索的过程也是一种不断传递的过程,将某个数列的中值和要查找的目标值比较,如果比它小,就在数列的后半部分做同样的操作,如果比它大,就在前半部分做相同的操作。那么这个回归的条件是什么呢?应该说有两个,一个是找到了,也就是某一个中值等于目标值,一个是没有找到,可以定义为找到了第一个元素和最后一个元素还是没有找到,那么也结束递归,其代码如下:

二分查找

 1 int BinarySearch(int a[],int n,int low,int high)
 2 {
 3     int mid=(low+high)/2;
 4     if(n==a[mid])
 5         return mid;
 6     if((mid==high||mid==low)&&n!=a[mid])
 7         return 0;
 8
 9     if(n>a[mid])
10           BinarySearch(a,n,mid+1,high);
11     else
12           BinarySearch(a,n,low,mid);
13
14 }

通过代码可以看到思路和我上面语言描述的基本是一致的,这就是递归的好处,可以使得代码更加清晰。

六、“高帅富”的装备

      如果你看过一些时间复杂度可以到O(NLOGN)的排序算法,可以看到它们不仅效率高,代码也很简洁,因为使用递归使得很多复杂的过程变得简单,使得某个算法可以更容易的实现出来,我先要说的是归并排序。

归并排序简单的将就是将一个数列不断的平均分为两个小数列,然后每个小数列独立排序之后再合并在一起排序,也就是用若干有序的子序列得到一个有序的数列,为了说明,还是用一个例子好了,就用百度的这个例子好了:

如 设有数列{6,202,100,301,38,8,1}

初始状态: [6] [202] [100] [301] [38] [8] [1]

  i=1 [6 202 ] [ 100 301] [ 8 38] [ 1 ]    

i=2 [ 6 100 202 301 ] [ 1 8 38 ] 

  i=3 [ 1 6 8 38 100 202 301 ]   

整个过程就是不断的划分为子序列,不断的用子序列排序,这明显是一个递归的过程,传递的过程是不断传递子序列,那么回归条件是什么呢?貌似这里不太能够看出来,从上面的过程可以大概看出来如果当数列的个数只有1的话,那么就要开始回归了,所以我们采用了一个方法,既然需要找中间的那个值,那么就要保存左边的索引和右边的索引,利用这两个索引,可以确定出数组中有多少个值,那么先看一下代码吧。

归并排序

 1 void MergeSort(int numbers[],int array_size)
 2 {
 3     int* tmpArray =new int[array_size-1];
 4     MergeSort(numbers,tmpArray,0,array_size-1);
 5
 6 }
 7
 8 void MergeSort(int numbers[],int tmpArray[],int left,int right)
 9 {
10     if(left<right)
11     {
12         int center=(left+right)/2;
13         cout<<"0"<<endl;
14         MergeSort(numbers,tmpArray,left,center);
15         cout<<"1"<<endl;
16         MergeSort(numbers,tmpArray,center+1,right);
17         cout<<"2"<<endl;
18         Merge(numbers,tmpArray,left,center+1,right);
19         cout<<"3"<<endl;
20     }
21 }
22
23 void Merge(int numbers[],int tmpArray[],int leftPos,int rightPos,int rightEnd)
24 {
25     int leftEnd=rightPos-1;
26     int tmpPos=leftPos;
27     int numElements=rightEnd-leftPos+1;
28
29     while(leftPos<=leftEnd&&rightPos<=rightEnd)
30     {
31         if(numbers[leftPos]<=numbers[rightPos])
32             tmpArray[tmpPos++]=numbers[leftPos++];
33         else
34             tmpArray[tmpPos++]=numbers[rightPos++];
35     }
36     while(leftPos<=leftEnd)
37          tmpArray[tmpPos++]=numbers[leftPos++];
38     while(rightPos<=rightEnd)
39          tmpArray[tmpPos++]=numbers[rightPos++];
40
41     for(int i=0;i<numElements;i++,rightEnd--)
42          numbers[rightEnd]=tmpArray[rightEnd];
43
44     for (int i = 0; i < numElements; i++){
45             cout<<numbers[i]<<" ";
46         }
47         cout<<endl;
48 }

代码开始有些复杂了,真正有点算法的感觉了,先看看三个函数,第一个函数没有什么特别的含义,只是屏蔽掉一些细节而已,从第二个MergeSort开始,可以看到就像我们描述的思路那样,第一个是比较是否数组里只有一个值,需要回归啦,然后求出中值,左边的排序成有序的子序列,然后排序右边的,最后将两个子序列合并起来,是不是思路特别的清晰?那么接下来看看Merge函数,如果有两个有序的子序列如何将他们合并成一个?因为这两个子序列都是有序的,记为子序列A和子序列B,A[0]到A[size-1]是有序的,B[0]到B[size-1]也是有序的,那么对比的过程就简单了,不断的对比不断的合并就可以了。

对于函数执行的递归过程,肯定很多人还是一头雾水,这很正常,毕竟没有一个清晰的直观的认识,那么让我们看看递归的每一步都是怎么走的吧。

对于百度给的那个例子,从头看起,我们调用的代码是 MergeSort(a,7); 所以,

1.   left最开始等于0,right等于6,进入MergeSort以后left<right,进入if,输出0,center=3,调用   MergeSort(numbers,tmpArray,left,center);
1.1 left等于0,right等于center等于3,依然满足left<right,进入if,输出0,center=1,继续调用   MergeSort(numbers,tmpArray,left,center);

1.2 left等于0,right等于center等于1,依然满足left<right,进入if,输出0,center=0,继续调用   MergeSort(numbers,tmpArray,left,center);

1.3 left等于0,right等于center等于0,不满足left<right,掉回1.2往下执行,到此步,输出了三个0

1.2.1  left等于0,right等于center等于1,执行MergeSort(numbers,tmpArray,left,center);下一行语句,输出1,执行       MergeSort(numbers,tmpArray,center+1,right);

1.2.2 left等于center+1等于1,right=1,不满足left<right,回到1.2.1,执行MergeSort(numbers,tmpArray,center+1,right);下面的语句,输出2,然后进入

Merge(numbers,tmpArray,left,center+1,right);排序完成之后输出3。

以上是一次完整的递归过程,对着输出可以看到这个过程的执行,作为理解递归的练习,完全可以对照着后面的输出熟悉递归的过程,对于递归的执行,我觉得可以理解为执行到调用自己的函数的时候就不断的困在自己的这个函数中,直到到达某一个条件时,被自己释放,回到上一个过程才能进行,这个过程就像有的人失恋了,每天都和自己纠结,每天都在痛苦和不安中度过,这个过程中他是不能往下走的,一旦到某一个条件,比如时间慢慢的冲淡了感觉,他又可以继续进行了。

转载于:https://www.cnblogs.com/ZXYloveFR/archive/2012/09/26/2704241.html

你所能用到的数据结构(四)相关推荐

  1. 教老婆学java系列之奇妙的数据结构四

    教老婆学java系列之奇妙的数据结构四 数据结构最后一节 思考题:后台处理一件事,耗时较长,怎么将信息显示在页面上 问题解析: 1.前端触发,后端处理时间较长,前端不能等处理完成. 2.如果不等处理完 ...

  2. Python数据结构 四种链表的集合

    python数据结构四个链表的集合 结点的创建 import os# 创建节点 class Node:def __init__(self, data):self.data = dataself.nex ...

  3. 第九章:数据结构四兄弟——列表(下),痴月熊学python

    痴月熊学Python 文章目录 痴月熊学Python 往期文章 前言 一.对象.方法() 二.列表方法 2.1.列表内置函数 2.2.列表追加元素 2.3.列表插入元素 2.4.列表删除元素 三.列表 ...

  4. 第八章:数据结构四兄弟——列表(上),痴月熊学python

    痴月熊学Python 文章目录 痴月熊学Python 往期文章 前言 一.创建列表 二.读取列表 三.删除和修改元素 总结 系列文章 往期文章 第一章:Python-新人报道 第二章:小学生都会的数学 ...

  5. python数据结构 树_python 数据结构四 之 二叉树和树

    python数据结构教程第四课 树形结构是复杂结构中最简单的一类,这是一类非常重要的结构,在实际中使用广泛,反映了许多计算过程的抽象结构 一.简介 1.树 2.二叉树 二.二叉树和树的抽象数据类型(A ...

  6. 你所能用到的数据结构(六)

    八.数据结构不一定很枯燥      正如我现在实习的公司的一个同事说的那样,数据结构是一本催眠的书,我想对于大多数人应该是这样的,当然对我也是,看着一大堆的算法,结构模型,不想睡觉那应该可以归结为ge ...

  7. 使用javascript模拟常见数据结构(四)

    七.树 树是一种非线性的分层的数据结构,在现实生活中比较常见的例子比如家谱和公司的组织架构图,如下所示: 一个树结构存在着一系列的父子结构,并且有着一个根节点,这种结构本质上表明了一对多的关系. 那, ...

  8. 数据结构四——散列表(上)

    文章出处:极客时间<数据结构和算法之美>-作者:王争.该系列文章是本人的学习笔记. 1散列表的由来 从数组随机访问特性说起. 数组的随机访问特性是:数组a,a[5]可以直接访问到数组的第6 ...

  9. 数据结构四——散列表(下)

    文章出处:极客时间<数据结构和算法之美>-作者:王争.该系列文章是本人的学习笔记. 7 散列表+链表的应用 很多情况下散列表会和链表一起使用.散列表可以通过key查找value.链表可以按 ...

最新文章

  1. Java环境变量的配置 (Win10环境下)
  2. 学好机器学习,这里有想要的一切
  3. CentOS7,linux下nginx的安装过程——2.配置user,路径,openssl,make install,关闭防火墙,测试——源码
  4. pyspider爬虫框架
  5. 几个简单的OpenCV程序
  6. Codeforces Round #564 (Div. 2) A. Nauuo and Votes
  7. C#.NET Split 的几种使用方法
  8. 【转】《从入门到精通云服务器》第三讲-配置与升级云服务器
  9. 一文速学-时间序列分析算法之加权移动平均法详解+Python代码实现
  10. win7 64位 安装Infragistics NetAdvantage,报Error 1606 Could Not Access Network Location %SystemDrive%/ine
  11. 《DO圣堂刺客2》国服首测今天拉开
  12. (已成功)windows下,VS2012+Qt5.5.1的安装、路径配置、项目配置(其它版本可参考)
  13. python实现matlab_python 实现matlab的mapminmax方法
  14. Greenplum 数据库和schema管理
  15. 真正拖垮你的,其实是沉没成本
  16. 如何下载历史版本和最新版本的iar
  17. 数字孪生 软著登记表 模板
  18. python web py入门(4)-从数据库读取数据显示到网页
  19. csv文件的保存与读取
  20. word文件怎么另存

热门文章

  1. axure 素材_Axure原型:超漂亮的系统首页
  2. 推理计算过程_初中物理电学计算题第六讲:极值问题推理和限制条件
  3. win10系统中环境变量path变成一行显示(配置环境)
  4. linux新建用户代码,Linux_用dsadd添加用户的代码,描述: 此工具命令将一些具体 - phpStudy...
  5. sdn体系的三个平面_软件定义网络基础---SDN控制平面
  6. 澳大利亚 计算机 博士,澳大利亚迪肯大学招收计算机博士
  7. 一部论述修养人生处世出世的集录_读《菜根谭》,人生本是修心的过程
  8. 两部手机怎样才能把数据都传过来_我把魅族换成荣耀,30G的数据文件该如何一键转移?...
  9. “千年虫”,计算机的巨大BUG!
  10. 如何用 J-Link 来串口调试?