![如何用栈实现递归与非递归的转换_第1页](http://file4.renrendoc.com/view/83676574df5f397176e0f5abe9b346a9/83676574df5f397176e0f5abe9b346a91.gif)
![如何用栈实现递归与非递归的转换_第2页](http://file4.renrendoc.com/view/83676574df5f397176e0f5abe9b346a9/83676574df5f397176e0f5abe9b346a92.gif)
![如何用栈实现递归与非递归的转换_第3页](http://file4.renrendoc.com/view/83676574df5f397176e0f5abe9b346a9/83676574df5f397176e0f5abe9b346a93.gif)
![如何用栈实现递归与非递归的转换_第4页](http://file4.renrendoc.com/view/83676574df5f397176e0f5abe9b346a9/83676574df5f397176e0f5abe9b346a94.gif)
![如何用栈实现递归与非递归的转换_第5页](http://file4.renrendoc.com/view/83676574df5f397176e0f5abe9b346a9/83676574df5f397176e0f5abe9b346a95.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、如何用栈栈实现递递归与非非递归的的转换一.为什什么要学学习递归归与非递递归的转转换的实实现方法法? 11)并不不是每一一门语言言都支持持递归的的. 22)有助助于理解解递归的的本质. 3)有助于于理解栈栈,树等等数据结结构. 二.递归归与非递递归转换换的原理理. 递递归与非非递归的的转换基基于以下下的原理理:所有有的递归归程序都都可以用用树结构构表示出出来.需需要说明明的是, 这个个原理理并没没有经过过严格的的数学证证明,只只是我的的一个猜猜想,不不过在至至少在我我遇到的的例子中中是适用用的. 学习过过树结构构的人都都知道,有三种种方法可可以遍历历树:前前序,中中序,后后序.理理解这三三种遍历
2、历方式的的递归和和非 递递归的表表达方式式是能够够正确实实现转换换的关键键之处,所以我我们先来来谈谈这这个.需需要说明明的是,这里以以特殊的的 二叉叉树来说说明,不不过大多多数情况况下二叉叉树已经经够用,而且理理解了二二叉树的的遍历,其它的的树遍历历方式就就不难 了. 1)前前序遍历历 a)递归归方式: ccodee:1:1f22a399cc22dvvoidd prreorrderr_reecurrsivve(BBitrree T) /* 先序遍遍历二叉叉树的递递归算法法 */ if (T) vissit(T); /* 访访问当前前结点 */ preeordder_reccurssivee(T
3、-lcchilld); /* 访问问左子树树 */ prreorrderr_reecurrsivve(TT-rrchiild); /* 访访问右子子树 */ /coode:1:11f2aa39ccc2dd b)非递递归方式式 ccodee:1:1f22a399cc22dvvoidd prreorrderr_noonreecurrsivve(BBitrree T) /* 先序遍遍历二叉叉树的非非递归算算法 */ innitsstacck(SS); pussh(SS,T); /* 根指针针进栈 */ whiile(!sttackkemppty(S) whiile(getttopp(S,p)&p)
4、/* 向左走走到尽头头 */ viisitt(p); /* 每每向前走走一步都都访问当当前结点点 */ puush(S,pp-llchiild); poop(SS,p); iif(!staackeemptty(SS) /* 向向右走一一步 */ ppop(S,pp); pussh(SS,p-rcchilld); /codde:11:1ff2a339ccc2d 2)中序序遍历 a)递归归方式 codde:11:1ff2a339ccc2dvoiid iinorrderr_reecurrsivve(BBitrree T) /* 中序遍遍历二叉叉树的递递归算法法 */ if (T) inoordeer
5、_rrecuursiive(T-lchhildd); /* 访问左左子树 */ vissit(T); /* 访访问当前前结点 */ inoordeer_rrecuursiive(T-rchhildd); /* 访问右右子树 */ /coode:1:11f2aa39ccc2dd b)非递递归方式式 ccodee:1:1f22a399cc22dvvoidd iinorrderr_noonreecurrsivve(BBitrree T) innitsstacck(SS); /* 初始化化栈 */ ppushh(S, T); /* 根根指针入入栈 */ whille (!sttackkemppty(S
6、) whiile (geettoop(SS, pp) & pp) /* 向左走走到尽头头 */ puush(S, p-lchhildd); popp(S, p); /* 空空指针退退栈 */ iif (!sttackkemppty(S) popp(S, p); vvisiit(pp); /* 访问当当前结点点 */ puush(S, p-rchhildd); /* 向右走走一步 */ /codde:11:1ff2a339ccc2d 3)后序序遍历 a)递归归方式 coode:1:11f2aa39ccc2ddvooid posstorrderr_reecurrsivve(BBitrree T)
7、/* 中序遍遍历二叉叉树的递递归算法法 */ if (T) posstorrderr_reecurrsivve(TT-llchiild); /* 访访问左子子树 */ pposttordder_reccurssivee(T-rcchilld); /* 访问问右子树树 */ viisitt(T); /* 访问当当前结点点 */ /codde:11:1ff2a339ccc2d b)非递递归方式式 ccodee:1:1f22a399cc22dttypeedeff sttrucct BTTNodde* ptrr; eenumm 00,1,2 marrk; PPMTyype; /* 有有marrk域的的
8、结点指指针类型型 */ voidd poostoordeer_nnonrrecuursiive(BiTTreee T) /* 后续续遍历二二叉树的的非递归归算法 */ PMMTyppe aa; iinittstaack(S); /* SS的元素素为PMMTyppe类型型 */ puush (S,T,0); /* 根结点点入栈 */ whiile(!sttackkemppty(S) popp(S,a); swwitcch(aa.maark) casse 00: ppushh(S,a.ptrr,1); /* 修改改marrk域 */ if(a.pptr-lcchilld) puush(S,a.pp
9、tr-lcchilld,00); /* 访问问左子树树 */ brreakk; ccasee 1: puush(S,a.pptr,2); /* 修改mmarkk域 */ iif(aa.pttr-rchhildd) pussh(SS,aa.pttr-rchhildd,0); /* 访问右右子树 */ breeak; caase 2: vissit(a.pptr); /* 访问结结点 */ /codde:11:1ff2a339ccc2d 44)如何何实现递递归与非非递归的的转换 通常常,一个个函数在在调用另另一个函函数之前前,要作作如下的的事情:a)将将实在参参数,返返回地址址等信息息传递 给被被
10、调用函函数保存存; bb)为被被调用函函数的局局部变量量分配存存储区;c)将将控制转转移到被被调函数数的入口口. 从被调调用函数数返回调调用函数数之前,也要做做三件事事情:aa)保存存被调函函数的计计算结果果;b)释放被被调 函数的的数据区区;c)依照被被调函数数保存的的返回地地址将控控制转移移到调用用函数. 所所有的这这些,不不论是变变量还是是地址,本质上上来说都都是数数据,都是保保存在系系统所分分配的栈栈中的. ok,到这里里已经解解决了第第一个问问题:递递归调用用时数据据都是保保存在栈栈中的,有多少少个数据据需要保保存 就要设设置多少少个栈,而且最最重要的的一点是是:控制制所有这这些栈的
11、的栈顶指指针都是是相同的的,否则则无法实实现 同步. 下下面来解解决第二二个问题题:在非非递归中中,程序序如何知知道到底底要转移移到哪个个部分继继续执行行?回到到上 面说的的树的三三种遍历历方式,抽象出出来只有有三种操操作:访访问当前前结点,访问左左子树,访问右右子树.这三 种操操作的顺顺序不同同,遍历历方式也也不同.如果我我们再抽抽象一点点,对这这三种操操作再进进行一个个概括,可以 得到到:a)访问当当前结点点:对目目前的数数据进行行一些处处理;bb)访问问左子树树:变换换当前的的数据以以进行下下一次 处理理;c)访问右右子树:再次变变换当前前的数据据以进行行下一次次处理(与访问问左子树树所
12、不同同的方式式). 下面面以先序序遍历来来说明: ccodee:1:1f22a399cc22dvvoidd prreorrderr_reecurrsivve(BBitrree T) /* 先序遍遍历二叉叉树的递递归算法法 */ if (T) vissit(T); /* 访访问当前前结点 */ preeordder_reccurssivee(T-lcchilld); /* 访问问左子树树 */ prreorrderr_reecurrsivve(TT-rrchiild); /* 访访问右子子树 */ /coode:1:11f2aa39ccc2dd vvisiit(TT)这个个操作就就是对当当前数据
13、据进行的的处理, prreorrderr_reecurrsivve(TT-llchiild)就是把把当前 数据变变换为它它的左子子树,访访问右子子树的操操作可以以同样理理解了. 现在在回到我我们提出出的第二二个问题题:如何何确定转转移到哪哪里继续续执行?关键在在于一下下三个地地方:aa) 确确定对当当前数据据的访问问顺序,简单一一点说就就是确定定这个递递归程序序可以转转换为哪哪种方式式遍历的的树结 构;bb)确定定这个递递归函数数转换为为递归调调用树时时的分支支是如何何划分的的,即确确定什么么是这个个递归调调用 树树的左左子树和右右子树c)确确定这个个递归调调用树何何时返回回,即确确定什么么结
14、点是是这个递递归调用用树的 叶子子结点. 三.三个个例子 好了上上面的理理论知识识已经足足够了,下面让让我们看看看几个个例子,结合例例子加深深我们对对问题的的认识 .即使使上面的的理论你你没有完完全明白白,不要要气馁,对事物物的认识识总是曲曲折的,多看多多想你一一定可以以明 白白(事实实上我也也是花了了两个星星期的时时间才弄弄得比较较明白得得). 1)例例子一: ccodee:1:1f22a399cc22dff(n) = n + ; (n = 2); 这个例子子相对简简单一些些,递归归程序如如下: intt f_reccurssivee(innt nn) innt uu1, u2, f; if
15、 (n 2) ff = n + 1; ellse uu1 = f_reccurssivee(iint)(n/2); uu2 = f_reccurssivee(iint)(n/4); ff = u1 * uu2; retuurn f; /codde:11:1ff2a339ccc2d 下下面按照照我们上上面说的的,确定定好递归归调用树树的结构构,这一一步是最最重要的的.首先先,什么么是叶子子结点 ,我们们看到当当n = 0) sswittch(flaagccp) casse 00: /* 访问的的是根结结点 */ iif (staackcp = 2) /* 左子树树入栈 */ flaagccp =
16、 11; /* 修改标标志域 */ cp+; staackcp = (innt)(staackcp - 11 / 2); fflaggcpp = 0; elsse /* 否否则为叶叶子结点点 */ sttackkcpp += 11; fflaggcpp = 2; breeak; caase 1: /* 访问问的是左左子树 */ if (sttackkcpp = 22) /* 右子子树入栈栈 */ fllagcp = 2; /* 修改改标志域域 */ cpp += 2; sttackkcpp = (iint)(sttackkcpp - 2 / 44); flaagccp = 11; ellse
17、/* 否则为为叶子结结点 */ sstacckccp += 1; flaagccp = 22; brreakk; ccasee 2: /* */ iif (flaagccp - 1 = 2) /* 当前是是右子树树吗? */ /* * 如果果是右子子树, 那么对对某一棵棵子树的的后序遍遍历已经经 * 结束束,接下下来就是是对这棵棵子树的的根结点点的访问问 */ sstacckccp - 2 = staackcp * staackcp - 11; flaagccp - 2 = 2; cp = ccp - 2; elsse /* 否则退退回到后后序遍历历的上一一个结点点 */ cpp-; brre
18、akk; retuurn staack0; /coode:1:11f2aa39ccc2dd 算法法分析:a)fflagg只有三三个可能能值:00表示第第一次访访问该结结点,11表示访访问的是是左子树树,2表表示 已已经结束束了对某某一棵子子树的访访问,可可能当前前结点是是这棵子子树的右右子树,也可能能是叶子子结点.b)每每 遍历历到某个个结点的的时候,如果这这个结点点满足叶叶子结点点的条件件,那么么把它的的flaag域设设为2;否则根根据 访访问的是是根结点点,左子子树或是是右子树树来设置置flaag域,以便决决定下一一次访问问该节点点时的程程序转向向.2)例子子二 快速排序序算法 递归算算法
19、如下下: codde:11:1ff2a339ccc2dvoiid sswapp(innt aarraay, iint loww, iint higgh) innt ttempp; tempp = arrrayloww; arrrayloww = arrrayyhiigh; aarraayhhighh = teemp; int parrtittionn(innt aarraay, iint loww, iint higgh) innt pp; p = arrrayloww; whille (loww higgh) wwhille (loww = pp) higgh-; sswapp(arrray
20、y,loow,hhighh); whiile (loow hiigh & arrrayloww = p) loow+; sswapp(arrrayy,loow,hhighh); retuurn loww; voidd qssortt_reecurrsivve(iint arrray, intt loow, intt hiigh) intt p; if(llow hhighh) p = pparttitiion(arrray, loow, higgh); qssortt_reecurrsivve(aarraay, loww, pp - 1); qssortt_reecurrsivve(aarra
21、ay, p + 1, hiigh); /coode:1:11f2aa39ccc2dd 需需要说明明一下快快速排序序的算法法: pparttitiion函函数根据据数组中中的某一一个数把把数组划划分为两两个部分分, 左左边的部部分均不不大于这这个数,右边的的数均不不小于这这个数,然后再再对左右右两边的的数组再再进行划划分.这这 里我我们专注注于递归归与非递递归的转转换,pparttitiion函函数在非非递归函函数中同同样的可可以调用用(其实实 paartiitioon函数数就是对对当前结结点的访访问). 再次次进行递递归调用用树和栈栈的分析析: 递递归调用用树:aa)对当当前结点点的访问问是调
22、用用parrtittionn函数;b)左左子树: qssortt_reecurrsivve(aarraay, loww, pp - 1);c)右右子树:qsoort_reccurssivee(arrrayy, pp + 1, higgh); d)叶子结结点:当当loww higgh时;e)可可以看出出这是一一个先序序调用的的二叉树树 栈:要保存存的数据据是两个个表示范范围的坐坐标. ccodee:1:1f22a399cc22dvvoidd qssortt_noonreecurrsivve(iint arrray, intt loow, intt hiigh) intt m50, nn500,
23、cp, p; /* 初初始化栈栈和栈顶顶指针 */ cp = 00; mm0 = loww; nn0 = higgh; whille (mccp nncpp) wwhille (mccp 00) /* 压压栈, 直到mm1ccp = 00 */ whhilee (nn1ccp 00) /* 压压栈, 直到nn1ccp = 00 */ cpp+; m11cpp = m11cpp - 1; n11cpp = n11cpp - 1 - 11; /* 计算算akmm(m - 11, 11),当当n = 0时时 */ m11cpp = m11cpp - 1; n11cpp = 1; /* 改栈顶顶为ak
24、km(mm - 1, n + 1),当mm = 0时 */ cp-; m1cp = m1cp - 1; n1cp = n1cp + 11 + 1; whiile (cpp 0 | mm1ccp 00); retuurn n10 + 11; /ccodee:1:03551fdd74999三.递归归程序的的分类及及用途 递递归程序序分为两两类:尾尾部递归归和非尾尾部递归归.上面面提到的的几个例例子都是是非尾部部递归,在一个个选择分分支中有有至少 一个的的递归调调用.相相对而言言,尾部部递归就就容易很很多了,因为与与非尾部部递归相相比,每每个选择择分支只只有一个个递归调调用, 我们在在解决的的时候就
25、就不需要要使用到到栈,只只要循环环和设置置好循环环体就可可以了.下面再再举几个个尾部递递归的例例子吧,比较 简单我我就不多多说什么么了. 11)例子子一 coode:1:7785ffd533e3eeg(m, n) = 00 (mm = 0, n = 00) = g(m - 1, 2nn) + n; (mm 0, n = 00)/codde:11:7885fdd53ee3e aa)递归归程序 coode:1:7785ffd533e3eeinnt gg_reecurrsivve(iint m, intt n) if (m = 0 & nn = 0) rretuurn 0; retturnn (gg
26、_reecurrse(m - 1, 2*n) + nn); /codde:11:7885fdd53ee3e b)非非递归程程序 coode:1:7785ffd533e3eeinnt gg_noonreecurrsivve(iint m, intt n) intt p; for (p = 00; mm 0 & nn = 0; m-, n *= 22) p += nn; retuurn p; /codde:11:7885fdd53ee3e 22)例子子二 codde:11:7885fdd53ee3e f(n) = nn + 1 (n = 00) n * ff(n/2) (n 00)/codde:1
27、1:7885fdd53ee3e a)递递归程序序 ccodee:1:7855fd553e33eiint f_rrecuursiive(intt n) if (n = 0) reeturrn 11; rretuurn (n * ff_reecurrse(n/22); /coode:1:7785ffd533e3ee b)非非递归程程序 codde:11:7885fdd53ee3eintt f_nonnreccurssivee(innt nn) innt mm; for (m = 11; nn 0; n /= 22) m *= nn; retuurn m+; /ccodee:1:7855fd553e
28、33e 分析完完了递归归程序的的分类,让我们们回头看看看在向向非递归归转换的的过程中中用到了了什么来来实现转转换: 1)循循环,因因为程序序要在某某个条件件下一直直执行下下去,要要代替递递归程序序,循环环必不可可少,对对于尾部部递归,循环结结束的 条条件十分分容易确确定,只只要按照照不同分分支的条条件写出出来就可可以了.而对于于非尾部部递归程程序,循循环结束束的条件件一 般是是当栈为为空时或或者是结结束了对对递归调调用树的的遍历从从树的根根结点退退出时,而且有有的时候候写成wwhille()的形式式 ,有时时写成ddo .wwhille的形形式(如如上面的的akmm函数),具体体怎样,很难说说
29、清楚,取决于于你对整整个递归归程序的的分析 . 2)递归调调用树,树的结结构在转转换的过过程中是是不可见见的,你你不必为为转换专专门写一一个树结结构,不不过能不不能把递递归调用用 中的树树遍历方方式以及及叶子结结点,左左子树,右子树树等元素素确定好好是你能能否正确确解决问问题的关关键(这这一点已已经在 上上面的分分析过程程中展露露无疑),确定定好这些些后,剩剩下的工工作大部部分就是是按照给给出的几几种不同同的遍历历树的方方式 把程程序进行行改写,这个过过程就考考验你对对树结构构还有遍遍历方式式是否很很好的掌掌握了(看出基基础的重重要了吗吗?如果果 回答是是,那么么和我一一样好好好的打好好基础吧
30、吧,一切切都还不不晚!).对对于尾部部递归而而言,可可以看作作没有递递归调用用树, 所所以尾部部递归的的难度大大大降低低了. 3)栈栈,非尾尾部调用用中需要要栈来保保存数据据,这一一点已经经很清楚楚了,需需要注意意几个问问题:aa)栈有有时可能能会出现现不够的的 情况,拿上面面的akkm函数数来说,我用的的50个个元素的的数组,你如果果把m和和n值设设置得大大一些,这个栈栈就不能能用了,有 时你你的算法法正确了了,不过过没有注注意到这这个问题题还是会会出错的的;反过过来说,在递归归调用中中,系统统或者编编译器的的优 化功功能不够够好的化化,在这这个栈上上可能会会消耗很很多空间间,这个个时候如如果你把把程序改改成非递递归的形形式,然然后再用用 动态分分配技术术分配栈栈可能就就会把程程序的性性能提高高一大块块-这这也是我我们学习习这门技技术的意意义之一一,因为为系统 是是机械化化的
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 知识产权战略在科技企业营销中的实践与探索
- A公司JSC系统研发项目管理优化研究
- 分布式驱动电动汽车执行器失效自适应容错控制研究
- 电子商务物流发展趋势研究
- 基于CT门脉期纹理特征预测肝泡型包虫病肝外转移的初步研究
- 电机设备的正确使用与维护培训
- 2025-2030年中国印花经编布行业深度研究分析报告
- 2024-2026年中国PET.MRI系统行业市场全景调研及投资规划建议报告
- 电子商务平台的沟通策略
- 农村道路修建申请书
- 2025年菏泽医学专科学校高职单招职业技能测试近5年常考版参考题库含答案解析
- 成都四川成都简阳市简城街道便民服务和智慧蓉城运行中心招聘综治巡防队员10人笔试历年参考题库附带答案详解
- 2025-2030全球废弃食用油 (UCO) 转化为可持续航空燃料 (SAF) 的催化剂行业调研及趋势分析报告
- 山东省临沂市兰山区2024-2025学年七年级上学期期末考试生物试卷(含答案)
- 2025年环卫工作计划
- 湖北省武汉市2024-2025学年度高三元月调考英语试题(含答案无听力音频有听力原文)
- 品质巡检培训课件
- 一年级下册劳动《变色鱼》课件
- 商务星球版地理八年级下册全册教案
- 天津市河西区2024-2025学年四年级(上)期末语文试卷(含答案)
- 2023青岛版数学三年级下册全册教案
评论
0/150
提交评论