递归再理解
其实关于递归,我也是比较模糊的,至今能理解和能用的递归算法,基本是靠记忆和经验,要是让我自己设计一个递归,估计又得难半天,很早都想总结一下,喜欢浏览技术网
站,总是能找到好东西,现在将递归算法总结如下,也不是多么深刻,多么高大上,可以说还是拙见吧:
》》》定义递归算法:(基本思想)
那么什么是递归算法呢,递归的基本思想是把规模大的问题转化为规模小的相似的子问题来解决。在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。另外这个解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。以上这就是递归算法的基本思想,也就这
么多,下面看一个图:
这既是一个最基本的的递归函数,函数体是:f(n) = f(n-1)*n;递归结束条件就是f(1) = 1;之后逐级返回,求出f(5),递归两个过程完成。
》》》递归算法的形象理解:
借用一个网友的说法:(关于门)
>>递归:你打开面前这扇门,看到屋里面还有一扇门(这门可能跟前面打开的门一样大小(静),也可能门小了些(动)),你走过去,发现手中的钥匙还可以打开它,你
推开门,发现里面还有一扇门,你继续打开,。。。, 若干次之后,你打开面前一扇门,发现只有一间屋子,没有门了。 你开始原路返回,每走回一间屋子,你数一次,走到入
口的时候,你可以回答出你到底用这钥匙开了几扇门。
>>循环:你打开面前这扇门,看到屋里面还有一扇门,(这门可能跟前面打开的门一样大小(静),也可能门小了些(动)),你走过去,发现手中的钥匙还可以打开它,
你推开门,发现里面还有一扇门,(前面门如果一样,这门也是一样,第二扇门如果相比第一扇门变小了,这扇门也比第二扇门变小了(动静如一,要么没有变化,要么同样的变
化)),你继续打开这扇门,。。。,一直这样走下去。 入口处的人始终等不到你回去告诉他答案。
以上的话很好理解,递归和循环很好的区别开了,总的来说,递归,有去还有回;循环,有去无回;
》》》递归算法的应用:
>>递归算法,总的来说就是:《往深处递->结束条件->归队》,但是,在实际应用中也不是说,一定是有去有回,其实还有有去无回,典型的就是分治思想。
递归依赖于要解决的问题,有的需要去的路上解决,有的需要回来的路上解决,还有来回的路上都不用解决问题,而是在递的最后和归的开始,也就是在最尽头解决问题。
看几个例子:
1.回来的路上解决问题:
问题描述:合并两个有序的单链表,使用递归的思想;
有序链表合并,思想很简单,直接看代码:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2) { if (pHead1 == nullptr) return pHead2; else if (pHead2 == nullptr) return pHead1; ListNode* pMergeHead = nullptr; if (pHead1->m_nValue < pHead2->m_nValue) { pMergeHead = pHead1; pMergeHead->m_pNext = Merge(pHead1->m_pNext, pHead2); } else { pMergeHead = pHead2; pMergeHead->m_pNext = Merge(pHead1, pHead2->m_pNext); } return pMergeHead; }每一次递归返回的都是一个节点,这个节点最终需要连接在上一个节点的后边,一次递到结束条件,回来依次连接各有序节点,这就是在归回来的路上解决问题;
2.去的路上解决问题:
简单,直接口述一个例子,比如一个二叉树,在某种需要下,需要将二叉树左边的一路全部压入栈中,在去的路上做的就是数据压栈;
3.在尽头解决问题:
//递归查找 Node* _Find_R(Node* root, const K& key) { if (root == nullptr) { return nullptr; } Node* cur = root; if (cur->key == key) { return cur; } else if (cur->key > key) { _Find_R(cur->_left,key); } else if (cur->key < key) { _Find_R(cur->_right,key); } else { return nullptr; } }以上是一个搜索二叉树的递归查找函数,左右子树遍历,只为在某一个地方匹配到数据,然后返回,问题解决;