递归(实践版)
这篇博客我不会写太多细节,我只做一件事,那就是教你如何写好一个递归.
二叉树的后序遍历
public void dfs(TreeNode root){
if(root==null)return;
dfs(root.left);
dfs(root.right);
System.out.println(root.val);
}
归并排序
public void merge(int[] nums,int left,int right){
if(left>=right)return;
int mid=left+(right-left)/2;
merge(nums,left,mid);
merge(nums,mid+1,right);
合并两个有序数组......
}
一,函数头的设计
找到相同的子问题,比如在二叉树的后序遍历,需要依次遍历左节点,右节点,根节点,所以打印单个节点就是相同的子问题,我们的函数参数就只需要(TreeNode root).归并排序的参数需要包含数组及区间边界(left/right),因为相同的子问题是处理数组的一个子区间
二,函数体的书写
忘掉递归展开的细节图,把递归函数抽象成一个整体,这是很重要的一点,"抽象"思想在计算机中无处不在,所以不要把精力耗费在细节的钻研上,要学会搭积木,把整个递归函数当成一个黑盒,我们输入数据,递归函数返回一个唯一的值.比如二叉树的后序遍历,我们就是要打印左节点,所以调用递归函数(这时候的递归函数就抽象成我们输入值,它会打印值)并传入左节点,然后打印右节点,调用递归函数并传入右节点,最后打印自身,也就是根节点.再比如,归并排序,先是左排序,调用递归函数(我们传入数组,它返回排序后的数组)并传入左数组,然后右排序,调用递归函数传入右数组,最后将两个排序后的数组进行合并.
三,终止条件
不能再分割的问题,就是字面意思,比如二叉树的后序遍历,节点等于空时,归并排序,数组不存在时,直接返回.
递归是自我复制的艺术。当写下dfs(root.left)
时,本质是创造无数个遵循相同规则的黑箱副本。这就像分形图案:定义基础形状(终止条件)和生长规则(递归调用),复杂结构自然涌现。