递归基本概念

递归(recursive)

名词解释

// 编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。
// 递归是一种解决问题的有效方法,在递归过程中,函数将自身作为子例程调用
// 为了确保递归函数不会导致无限循环,它应具有以下属性:
// 一个简单的基本案例(basic case)(或一些案例) —— 能够不使用递归来产生答案的终止方案。
// 一组规则,也称作递推关系(recurrence relation),可将所有其他情况拆分到基本案例。
// 注意,函数可能会有多个位置进行自我调用。
// 在实现递归函数之前,有两件重要的事情需要弄清楚:
// 递推关系: 一个问题的结果与其子问题的结果之间的关系。
// 基本情况: 不需要进一步的递归调用就可以直接计算答案的情况。 有时,基本案例也被称为 // bottom cases,因为它们往往是问题被减少到最小规模的情况,也就是如果我们认为将问题划分为子问题是一种自上而下的方式的最下层。

举个例子

/*
*题目来自leetcode
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
*/

DEMO

// 递归解决
public static void reverseString(char[] arr, int left, int right) {
        // 递归出口
        if(arr == null || left >= right) {
            return;
        }
        // 交换收尾的值
        char tmp = arr[left];
        arr[left++] = arr[right];
        arr[right--] = tmp;
        // 重复调用
        reverseString(arr, left, right);
}
​
// 循环解决
public static void reverseString2(char[] arr) {
        if(arr == null) {
            return;
        }
        int left = 0;
        int right = arr.length - 1;
        while(left < right) {
            char tmp = arr[left];
            arr[left++] = arr[right];
            arr[right--] = tmp;
        }
}

项目实战

/**
   场景
   项目中用到了一个区域的表,表结构类似这样的:
   id name pid
   1  root 0
   2  sh   1
   3  hp   2
   而区域的id作为一个实体的属性.
   现要求新增该实体的一个属性来放上表中的name的值,并且要求一个节点都展示包括root.
   类似这种:root/sh/hp...
*/
public String getNodeName(String nId) {
    // 用于存放递归结果
    List<String> names = new ArrayList<>();
    recursiveNodeName(names, nId);
    // 将结果顺序翻转
    Collections.reverse(names);
    // 拼接结果,返回
    return String.join("/",names);
}
​
public void recursiveNodeName(List<String> names, String nId) {
    Node node = dao.selectNodeById(nId);
    if(Objects.nonNull(node)) {
        names.add(node.getName());
        // 判断当前node的pid是否是0,此为结束条件
        if("0".equals(node.getPid)) {
            return;
        }else {
            // 继续找上一节点
            recursiveNodeName(names, node.getPid());
        }
    }else {
        return;
    }
}

练习题

// 题目来自LeetCode递归练习题
T1.给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.
T2.反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
T3.斐波那契数,通常用 F(n) 表示,形成的序列称为斐波那契数列。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
示例:
输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1.
T4.假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶
T5.给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7]
    3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值