递归的理解---1
开门见山:
不要试图在脑中复现递归函数的调用栈,你只要相信递归函数的作用即可。
重点提示:
1.不要试图在脑中复现递归函数的调用栈,你只要相信递归函数的作用即可。重要的事情多说一遍,否则你容易陷入一种脑循环(你的小脑袋瓜嗡嗡的也循环不出来),如果是单层的递归调用你可能还好理解,但是如果是函数多次调用自己进行递归,你会发现跟着开发工具步步跟调,也很难去理解中间这个调用过程,且调用次数稍多,就涉及到大量的步骤,我们只要相信算法本身,相信上一步会给我们返回准确的结果,相信它的作用即可。
2.一定要理解递归的真切含义,是先递过去再从后面依次送过来,就像别的文章中经常举的一个例子,一个长长的队伍,你在队伍中间,想知道你是第几个,你拍前一个人的肩膀,前面的人也不知道,继续问前面的,直到问道第一个人,第一个人扭头告诉第二个人,然后从第二个人依次向后位置依次加1,直到最后再走回你这里,你知道了你的位置。
具体讲解:
第一、 明确递归函数的作用和参数的含义,然后坚定的相信它的作用
再次强调不要试图在脑中重演递归过程,当然你也做不到。你要做的便是:明确递归函数的作用,包括参数是什么,是否有返回值,有的话返回值是什么,然后相信它就行了。
第二、找到递归基
所谓递归基,就是递归结束的条件。比如 n == 0的时候之类的情况。
第三、明确递归函数返回后,该做点啥
里层的递归函数返回后,需要与当前的层做一些互动,然后才能将彼此联系起来。
思考技巧
就是将问题拆分成两个部分, 即1
与剩余整体
,其中剩余整体
又可以用1
与剩余整体
的思想来考虑.如此思考,那么剩余整体
完全可以用递归的方法去解决.
重中之重的点如下联系到第一点技巧
的提示,即_相信剩余整体已经处理完毕_
之后如何与_1的部分_
衔接问题.
现在这么一说可能会很抽象,在例子中会给出很形象的解释.
示例分析
求和
使用for
循环很容易求和1+2+3+...+n
, 那么使用递归
的方法呢?
- 定义求和函数
sum
.明确函数作用, 给定一个n
,sum(n)
返回1+2+...+n
的总和 - 找到
递归基
, 当n=1
时,返回1
- 利用
1
与剩余整体
的思想找到递推式.1+2+...+n
可以分解成1+2+...+n-1
与n
的和,其中最后的n即可代表1
的思想,1+2+...+n-1
代表剩余整体的思想.相信sum
函数的作用,即sum(n-1)
返回1+2+...+n-1
的结果.总结一下即为n
与sum(n-1)
的问题.
代码如下
public static int sum(int n){
if (n == 1){
return 1;
}
return n + sum(n - 1);
}
sum(10)
# 55
当我们写 return n + sum(n-1)
要想到关于递归的第一个技巧,我们假设sum(n-1)
是没有问题的,正常返回1+2+...+n-1
的和,不要自己绕进去.递归求和就是这么简单.
数组内部的最大值
用循环的方法,同样是很简单的.那么递归呢
假设有数组
l = [23, 34, 2, 4, 56, 23, 1, 56, 78]
- 定义一个求数组最大值的函数
max
.明确其作用,给定一个数组,返回其中的最大值
- 找到
递归基
,即当数组的元素为1
时,返回这个数组元素即可 - 利用
1
和整体思想
去寻找递归关系式.将数组第一项的23
作为1
, 将剩下的[34, 2, 4, 56, 23, 1, 56, 78]
作为一个整体.则 比较23
与max([34, 2, 4, 56, 23, 1, 56, 78])
两者的大小,如果前者大,返回23
,否则返回后者.
代码如下
public class Recursion {
public static void main(String[] args) {
int[] arr = {1, 5, 3, 9, 13, 7, 25, 19, 37, 123, 16, 255, 1234, 125, 1379, 233, 345, 235, 7890, 11111};
int maxNumber = MaxNumber(arr, 0, arr.length - 1);
System.out.println("maxNumber = " + maxNumber);
}
public static int MaxNumber(int[] arr, int low, int high) {
//随着递归的进行,数组的边界会改变.
//直接定义在接口处,递归更容易实现和理解.因为这样一来,容易切割实现1和剩余整体的思想.
if (low == high) {
//递归基,此时数组中只有一个元素,返回即可
return arr[low];
}
int left = arr[low];//1的思想
int right = MaxNumber(arr, low + 1, high);//剩余整体的思想
return Math.max(left, right);
}
}
单向链表的翻转
输入一个链表,反转链表后,输出新链表的表头
假设有一个链表
1->2->3->4->5->NULL
- 定义一个函数ReverseList, 翻转一个链表.返回新链表的头结点
- 找到递归基,如果一个链表只有一个节点,那么直接返回即可.
- 利用1和剩余整体的思想去解决此问题.
我们将原始链表的头结点1作为1,将2->3->4->5->NULL作为剩余整体.即第一个节点与ReverseList(2->3->4->5->NULL)如何链接的问题.
我们相信ReverseList函数作用,即ReverseList(2->3->4->5->NULL)已经被翻转成功,而且返回新链表的头结点.
我们如何连接这两个部分呢?
代码如下
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
// 递归算法
class Solution {
public:
ListNode* ReverseList(ListNode* head) {
// 递归基
if (head == NULL || head->next == NULL) return head;
// 当前节点的下一节点
ListNode* next_node = head->next;
ListNode* new_head = ReverseList(next_node);
next_node->next = head; # 下面这两句是建立两部分的连接
head->next = NULL; # 当前节点的下一节点需要保存一下
return new_head;
}
};
// 迭代算法
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
if (!pHead || !pHead->next) {
return pHead;
}
ListNode* pPre = NULL;
ListNode* pNode = pHead;
ListNode* pNext = NULL;
while (pNode) {
pNext = pNode->next;
pNode->next = pPre;
pPre = pNode;
pNode = pNext;
}
return pPre;
}
};
二叉树的最大深度
输入一棵二叉树,求该树的深度.从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度.
class Solution {
public:
// step1: 定义TreeDepth函数,它的作用是找到并且返回给定树节点对应的树的最大深度
int TreeDepth(TreeNode* pRoot)
{
// 找到递归基
if (pRoot == NULL) return 0;
// 递归关系式 Depth(root) Depth(root->left) Depth(root->right)
int left = TreeDepth(pRoot->left);
int right = TreeDepth(pRoot->right);
// 根据递归关系式与返回值,决定最终返回 1+max(left, right)
return 1 + max(left, right);
}
};
例子未完待续...
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构