递归使用的感悟总结---缓慢但持续更新

这篇博客探讨了递归在解决二叉树问题中的应用,如找到二叉搜索树中第K小的元素。作者通过实例解释了递归的三个关键部分:终止条件、递归调用和执行操作。此外,还分析了递归实现中序遍历的思路,强调了正确遍历顺序和添加节点的重要性。递归的理解对于算法设计至关重要。
摘要由CSDN通过智能技术生成

大部分笔记都写在印象笔记上,对于有意义的学习,会整理出来分享。

通过刷题来积累经验,如果错误,还望各位前辈指正。

一. 230题 二叉树中第K小的元素,对递归有了一点理解

    首先,递归理解为,递下去和归上来。要注意递下去的终止条件和归上来的开始条件是一个条件。但是我们递归的时候,不仅仅是为了遍历,而是想要每层做一些事情,一般我们都是在归上来的时候添加上我们想要做的事情。

    其次,我们的思维要保持清晰,其实递下去是很简单的,只需要再次调用该函数即可,只不过改变下参数而已,例如这里的参数可能是树的子树的根节点。所以重点在于递下去的终止条件(归上来的开始条件)和每层归上来要进行的额外操作。

    最后,我现在认为,一开始写递归函数时,应该现在函数体的开始写递下去的终止条件(即归上来的开始条件),然后写递下去(即调用此递归方法),最后写每层我们想要做的事情。

    补充:以上是针对递归函数没有返回值的情况。如果有返回值,则可以将返回值理解为我们想要做的事情的结果返回,也是作为我们想要做的事情的一部分。

举例:

1.  230. 二叉搜索树中第K小的元素   给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

     注:此方法不是最优,只是想要通过代码来表示我的理解:有三部分组成,①递下去的终止条件(即归上来的开始条件);②递下去;③我们想要做的事情

class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;
	TreeNode() {}
	TreeNode(int val) { this.val = val; }
	TreeNode(int val, TreeNode left, TreeNode right) {
		this.val = val;
		this.left = left;
		this.right = right;
	}
}



class Solution {
	List<Integer> orderedList = new ArrayList<Integer>();
    public int kthSmallest(TreeNode root, int k) {
    	findNode(root);
    	
    	orderedList.sort(new Comparator<Integer>() {//使用匿名Comparator类,并重写compare方法

			@Override
			public int compare(Integer o1, Integer o2) {//当满足compare的返回值条件时,就进行o1和o2的位置交换
				return Integer.compare(o1, o2);
			}
		});
    	return orderedList.get(k-1);
    }
    
    public void findNode(TreeNode root) {
        //递下去的终止条件(即归上来的开始条件)
    	if(root == null) {
    		return;
    	}
        //递下去
    	findNode(root.left);
    	findNode(root.right);
        //想要做的
    	orderedList.add(root.val);
    }
}

2. 经典递归例子,前n项积

public class LeetCodeTest {
     public static void main(String[] args) {
          int mul = LeetCodeTest.mul(4);
          System.out.println(mul);
     }
     
     public static int mul(int val) {
          //递下去的终止条件(归上来的起始条件)
          if(val == 1) {
              return val;
          }
          //递下去
          int mul = mul(val - 1);
          //我们想要做的
          return mul * val;
     }
}

二. 94题 递归法中序遍历----递归结构没有那么明显

    使用递归法进行中序遍历: 力扣    

    ①添加当前结点的前驱结点;②添加当前结点;③添加当前结点的后继结点

private void inorderTraversal(TreeNode root,  List<Integer> arr) {
    if(root == null) {//递下去的终止条件(归上去的开始条件)
        return;
    }

    inorderTraversal(root.left, arr);//递下去

    arr.add(root.val);//额外的操作--按照中序遍历添加 每个 结点

    inorderTraversal(root.right, arr);//递下去
}

        感觉合情合理,但是又感觉相互纠缠 。纠缠表现在:有两次调用递归方法,每一个递下去的时候都又会调用另一个。

        纠缠是外表,递下去也是外表,内因在于我们想要做的什么?我们想要按照中序遍历的方式添加值。重点在于 遍历结点的顺序 和 添加  ==> 按照正确的结点顺序添加值。

        很自然的(在中序遍历要求下我们要去找到第一个遍历的结点),我们要去:

inorderTraversal(root.left, arr);//递下去

        从而,递下去的终止条件(归上来的起始条件):

if(root == null) {//递下去的终止条件(归上去的起始条件)
    return;
}

        又是很自然的,当我们第一次归上来,即找到中序遍历的第一个结点,我们肯定要保存/记录下来:

arr.add(root.val);//额外的操作--按照中序遍历添加 每个 结点

        最后,当我们第一次归上来后,得到了中序遍历的第一个结点,我们又该去找第一个结点的后继结点,又是很自然的写出下一句:

inorderTraversal(root.right, arr);//递下去

    另外需要补充的是,我们在上面的代码中,想要去找后继结点,可能是想要用到不断将root.right去left找到后继节点;也有可能是要判断root.right是不是null。

    之后便不断地重复归上去了。

    所以从第一个值开始,从归上去(方向是从递归栈的栈顶向下---栈顶先出栈)的方向考虑①②③。

    理解有误请指正,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值