由翻转字符窜再次理解递归
来源:互联网 发布:时尚杂志知乎 编辑:程序博客网 时间:2024/05/29 02:59
要求:输入一个字符串,字符串反序输出。
比如:”hello” ⇒ “olleh”
首先就看代码是什么:
#include <iostream>#include <string>using namespace std;string str;void reverse(int n) // 递归翻转字符串{ if(n < str.size()) { reverse(n + 1); cout << str[n]; }}int main(){ cout << "Enter string: "; cin >> str; cout << "Reversing string is: "; reverse(0); cout << endl; return 0;}
当然有很多别的思路来完成这个任务,这里主要强调一种递归的巧妙思路:暂存下一条指令的地址。
通过这种较为偏向硬件的说法,来解答算法设计中递归的调用问题。
比如这里,第一次进入的是reverse(0),进入后判断0小于整个string的大小,因此将进入调用自身的指令。注意,此时不能就这样走了,还得暂存当前指令后面的那条指令地址,整个指令就是: cout << str[n];
也就是输出当前下标为n的字符。但是现在还不能输入,得等到自己对自己的探险结束后才回来输出。
递归调用像是盗梦空间中的梦中梦,而且这个梦中梦可以不止仅仅只有三层!
如此想象:第一次进入梦境,为了回到现实,需要在这个梦后面留个钩子,以从上一层梦境中跳出来。这就需要保存起来,我们用的是栈,看重的是它后入先出的特性。当你进入梦境最深层想回来你是不是得完成最深的那个梦境,虽然最深的梦境是最后进入的却最先完成。
进入第一层梦境后,把回到现实的地址存在了栈中,开始第一层梦境,然后发现条件满足还可以进入下一层梦境,于是深入第二层。当然你可不想困在第二层梦境中出不来,于是在第一层梦境的入口边上也做个标记,并把地址保存到栈顶。如此继续,直到最深层。
最深层执行时发现不能再进入更深了,于是该回去现实了。能一步跨回到现实吗?隔着那么多层梦境呢!饭要一口一口吃,一次只能回去一层。怎么回去?借用栈。
这个时候一看栈顶,好,这是下一步可以去的地方,把这个地址写入PC(程序计数器),顺利回到上一层梦境,这一层的梦境并未执行完毕,因此继续执行剩下部分。到这层梦境的尾声了,这层梦境顺利结束,下一步去哪里?看栈顶地址。跳到栈顶指示的位置开始执行再上一层的梦境的剩下部分。如此循环,直到靠近现实的那场梦境,再到回到现实,执行完现实的最后部分。栈已经空了,现实也结束。
这里只说到保存下一步的地址,实际上不止,会保存当前步骤的所有信息,我们称之为保护现场,比如现实中reverse(0),输出下标为0的字符,这个0就要被保存到寄存器,用来最终完成现实的剩下部分。
讲这么啰嗦,纯粹是因为,递归的思想太重要了。这不是我们记住全排列,翻转斐波那契数列,甚至是汉诺塔的解法就OK,行走江湖就不怕了,不是这样的。不理解执行的步骤推移,很难体会这种数据的游走过程。
虽然我支持设计算法时将这个部分忽略,关注顶层设计就好,要专注于当前任务,分而治之会更优雅,但绝不意味着不该仔细思考背后的硬件操作。
就像前几天写二叉树的递归插入设计,我用BST去接下一层递归的返回地址,总是得不到正确的结果,单步跟踪才理解用BST->Left或者BST->Right去接下一层调用的根一样。
你不理解数据的流动过程,便没有底气说,我这么设计就是对的。
以上。
- 由翻转字符窜再次理解递归
- 由一个翻转字符串程序看递归
- 字符翻转
- 字符翻转
- 编程理解——翻转前k个字符
- 由合并排序算法谈如何理解递归
- 由erlang的递归理解数组的全排列问题
- 由一个例子引起的对递归原理的理解。
- 再次详解递归
- 再次递归实现汉诺塔
- 递归翻转字串
- 递归翻转字符串
- 递归翻转单链表
- 递归实现字符串翻转
- 递归 字符串翻转 问题解决
- 递归实现字符串翻转
- 递归翻转一个字符串
- 字符编码再次剖析
- opencv3学习笔记(一)——opencv入门
- KMP模版 【KMP】
- 【51单片机学习过程记录】9 中断之定时计数器0的使用2
- Hdu 5150 Sum Sum Sum
- 解决场景加载慢的一种方法
- 由翻转字符窜再次理解递归
- Hdu 2569 彼岸
- POJ 3461 Oulipo【KMP】
- CSU 1598 最长公共前缀 【KMP】
- 75个国内主流源码类学习网站
- 排序算法
- 欢迎使用CSDN-markdown编辑器
- 【SQL】一条查询中统计同一字段不同记录值数量(频数统计)的写法
- 【验证码一】验证码Demo