递归写法总结
来源:互联网 发布:登陆艇升级数据经验值 编辑:程序博客网 时间:2024/05/29 04:40
先贴出Eric Lippert在stackoverflow的递归写法:
http://stackoverflow.com/questions/9304469/how-to-perform-a-recursive-search/9314805#9314805
其中写到递归大致模板:
Result M(Problem prob){ if (<problem can be solved easily>) return <easy solution>; // The problem cannot be solved easily. Problem smaller1 = <reduce problem to smaller problem> Result result1 = M(smaller1); Problem smaller2 = <reduce problem to smaller problem> Result result2 = M(smaller2); ... Result finalResult = <combine all results of smaller problem to solve large problem> return finalResult;}
举出二叉树深度的例子套用以上公式
int Depth(Tree tree){ // Start with the trivial case. Is the tree empty? if (tree.IsEmpty) return 0; // The tree is not empty. // Reduce the problem to two smaller problems and solve them: int depthLeft = Depth(tree.Left); int depthRight = Depth(tree.Right); // Now combine the two solutions to solve the larger problem. return Math.Max(depthLeft, depthRight) + 1;}
其又总结了要想写出递归所必备的三个条件:
- The problem has to get smaller every time you recurse.
- The problem has to eventually get so small that it can be solved without recursion
- The problem has to be solvable by breaking it down into a series of smaller problems, solving each one, and combining the results.
我自己的理解如下:
递归模板大致可以是:
T fun(T n) { if (边界条件) return T的对象 //(这里不是看n,而是看函数的作用是什么,它要返回什么,该返回对象与返回类型T有关,也有可能是null) else { //if(可能的其他边界条件) T obj = fun(//更小的事件);//可能不止一个递归方程,如求左右子树时 return obj; } }
- 函数类型T 是void型
此时实际上述模板的两个return都可以保留写成:
T fun(T n) { if (//边界条件) return; else { //if(可能的其他边界条件) fun(//更小的事件);//可能不止一个递归方程,如树时有左右子树,要两个 return; } }
当然,此时else中的return一般不写,因为,else中的语句运行完,隐式的就有return了,写出来只是显示地表达出递归的返回含义
- 边界条件
第一个边界条件就是考虑递归往下到最后一个结点时的情况,若在递归中(else语句中)还存在其他边界条件,可正面写,如
if (root != NULL) fun();
而不写成
if (root == NULL) return //返回void或对象 ;else fun();
这样在else语句中只会出现一个return语句
参考剑指offer面试题重建二叉树
BinaryTreeNode* ConstructCore( int* startPreorder, int* endPreorder, int* startInorder, int* endInorder){ // 前序遍历序列的第一个数字是根结点的值 int rootValue = startPreorder[0]; BinaryTreeNode* root = new BinaryTreeNode(); root->m_nValue = rootValue; root->m_pLeft = root->m_pRight = NULL; if(startPreorder == endPreorder) { if(startInorder == endInorder && *startPreorder == *startInorder) return root; //最后一个结点endNode返回后就到上一级的函数了 else throw std::exception("Invalid input."); } // 在中序遍历中找到根结点的值 int* rootInorder = startInorder; while(rootInorder <= endInorder && *rootInorder != rootValue) ++ rootInorder; if(rootInorder == endInorder && *rootInorder != rootValue) throw std::exception("Invalid input."); int leftLength = rootInorder - startInorder; //左子树的节点个数 int* leftPreorderEnd = startPreorder + leftLength; if(leftLength > 0) { // 构建左子树 root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd, startInorder, rootInorder - 1); } if(leftLength < endPreorder - startPreorder) { // 构建右子树 root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder, rootInorder + 1, endInorder); } return root;}
感想:
- 简单题目一般都可以用尾递归解决,复杂一些就不能把递归语句放在最后的return前面了(尾递归时,递归语句等价于结束前的循环语句)。
- 递归形式大部分都可以看成,一个结点()与剩余一整串的关系,然后该剩余一整串的处理采用递归方法。
递归语句写出后,其余语句的写法按照正常思维进行即可。
函数类型及作用是返回什么,递归语句相应的一定要返回什么,按格式写一般都不会出错,如:
fun(//更小的事件); //T是void
T obj = fun(//更小的事件); //T非void
举例: 从尾到头打印链表
void PrintListReversingly_Recursively(ListNode* pHead){ if (pHead == NULL) return; else { PrintListReversingly_Recursively(pHead->m_pNext); printf("%d\t", pHead->m_nValue); }}
分析:
1. 其中 PrintListReversingly_Recursively(pHead->m_pNext);
这句可以看成已经完成了pHead->m_pNext
为首的后续结点的反转工作,原理在于递归的每个结点的PrintListReversingly_Recursively(pHead->m_pNext)
实际都包含了打印当前结点的下一个节点:printf("%d\t", pHead->m_nValue);
从而我们可以直接把递归语句看成就是完成了后续pHead->m_pNext
为首的链表所有工作。现在就把问题分为只剩链表头一个结点pHead
和剩余的一整串以pHead->m_pNext
为首的链表了,现在就可以用顺序的思维写他们的关系了,而他们的关系是逆序打印,故递归语句先写,pHead
后写。
2. 只有当前问题与更小规模问题的处理方法一致时才能使用递归,处理方法即使除递归外的语句,如printf("%d\t", pHead->m_nValue);
。
- 递归写法总结
- 递归写法总结
- DB2 递归SQL写法
- 递归的写法
- 递归查询—写法
- oracle 递归写法
- 正则表达式递归写法
- DB2SQL递归写法
- sql 递归的写法
- 阶乘之递归写法
- 递归函数的写法
- 二分法递归写法
- 求递归写法
- mysql 递归函数写法
- java递归的写法
- 约瑟夫环递归写法
- SQL 递归写法
- 递归与非递归的各种写法
- BZOJ 3612 HEOI2014 大工程 树链剖分求LCA的优越
- UILable,UIButton字体加粗
- 乱码终极解决大全
- Android初试--Android中的ContentProvider(3)
- httpd.conf配置后php7,出现apache无法启动的现象
- 递归写法总结
- Big vs Big(链表)
- 机器学习实战(5)--SVM(Support vector machine)(二)
- struts2不显示.do .action 后缀
- 关于在c#中如何遍历listbox中选中项的问题
- 实现ViewPager懒加载的三种方法
- 集合框架--List常用子类
- C提高——const专题
- 获取网络状态