递归的本质与基本实现形式

简介: 递归的本质与基本实现形式


递归Recursion

如何理解递归?

  • 函数自身调用自身
  • 通过函数体来进行的循环
  • 以自相似的方法重复进行的过程

计算n!

n! = 1* 2* 3* …* n

def factorial(n):
  if n <= 1:
    return 1
  return n * factorial(n - 1)

啊喂,求n!用递推它不香吗…

确实,因为n!的推导路径我们已经知道了,for一遍就好了。

但如果是不太容易找到推导路径的问题呢?

比如下面的问题,一棵树,在根节点的时候,我们是不知道下边长什么样的…

递归的三个关键:

  • 定义需要递归的问题(重叠子问题)——数学归纳法思维
  • 确定递归边界
  • 保护与还原现场

C++/Java代码模板

void recursion(int level, int param) {
  // terminator
  if (level > MAX_LEVEL) {
    // process result
    return; 
  }
  // process logic in current level
  process(level, param);
  // drill down
  recur(level + 1, new_param);
  // restore the current level status
}

Python代码模板

def recursion(level, param1, param2, ...):
  # recursion terminator
  if level > MAX_LEVEL: 
    # process result
    return
  # process logic in current level
  process(level, data...) 
  # drill down
  self.recursion(level + 1, new_param1, ...) 
  # restore the current level status if needed

实战

78.子集

https://leetcode.cn/problems/subsets/

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        n = nums.size();
        recur(nums,0);
        return ans;
    }
private:
    void recur(vector<int>& nums, int i){
        if(i == n){
            ans.push_back(chosen);
            return;
        }
        recur(nums, i + 1);
        chosen.push_back(nums[i]);
        recur(nums, i + 1);
        chosen.pop_back();
    }
    int n;
    vector<int> chosen;
    vector<vector<int>> ans;
};

77.组合

https://leetcode.cn/problems/combinations/

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        this->n = n;
        this->k = k;
        recur(1);
        return ans;
    }
private:
    void recur(int i) {
        if(chosen.size() > k || chosen.size() + (n - i + 1) < k) return;
        if(i == n + 1){
            ans.push_back(chosen);
            return;
        }
        recur(i + 1);
        chosen.push_back(i);
        recur(i + 1);
        chosen.pop_back();
    }
    int n, k;
    vector<int> chosen;
    vector<vector<int>> ans;
};

46.全排列

https://leetcode.cn/problems/permutations/

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        n = nums.size();
        used = vector<bool>(n, false);
        recur(nums, 0);
        return ans;
    }
private:
    void recur(vector<int>& nums, int pos) {
        if(pos == n){
            ans.push_back(a);
            return;
        }
        for(int i = 0; i < n; i++){
            if(!used[i]){
                a.push_back(nums[i]);
                used[i] = true;
                recur(nums, pos + 1);
                used[i] = false;
                a.pop_back();
            }
        }
    }
    vector<bool> used;
    vector<int> a;
    int n;
    vector<vector<int>> ans;
};

47.全排列 ||

https://leetcode.cn/problems/permutations-ii/

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int> > res;
        do{
            res.emplace_back(nums);
        }while(next_permutation(nums.begin(), nums.end()));
        return res;
    }
};

递归基本形式总结

以上问题都是递归实现的“暴力搜索”(或者叫枚举、回溯等)

可以总结为以下三种基本形式

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

相关文章
|
存储 IDE 开发工具
研发提效工具3 - IDEA极速打开项目方案
# 背景 作为Idea、Android Studio、PyCharm等`jetbrains`系列IDE的开发者,每次打开一个工程项目时,一般都使用鼠标点击IDE左上角的Open菜单来才做。本文介绍一种基于Alfred的快捷方式进行打开操作。 # 效果图 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/affab
706 0
研发提效工具3 - IDEA极速打开项目方案
|
设计模式 机器学习/深度学习 SQL
软考高级系统架构设计师通关经验分享
为什么考系统架构设计师是国家设立的计算机技术与软件专业技术资格考试(简称软考)中的一个高级科目,属于工程师高级职称系列,具有一定含金量。浙江省每年通过软考高级的人数约为1000+人,其中系统架构设计师科目的通过人数约为200+人。从学习角度来说,通过准备系统架构设计师的考试的过程,可以查漏补缺,并且了解一些系统架构设计相关的基础知识,实现一定程度上的自我提升;从目的性的角度来说,通过考试,可以在一
14570 4
软考高级系统架构设计师通关经验分享
|
监控 安全 JavaScript
浅谈移动端设备标识码:DeviceID、IMEI、IDFA、UDID和UUID
场景 : 客户提出一个问题就是把用户的登录记录和设备绑定到一起,就是每个人都是固定的设备(可能是安全因素吧)。一开始想的是回去设备的IMEI号和用户账号绑定起来,结果发现IMEI不对外开发,只能另寻他法,最后通过获取设备序列号作为唯一标识。
浅谈移动端设备标识码:DeviceID、IMEI、IDFA、UDID和UUID
SPSS导入数据 错误号 105
SPSS导入数据 错误号 105
392 0
|
网络协议 算法 网络性能优化
C语言 网络编程(十五)套接字选项设置
`setsockopt()`函数用于设置套接字选项,如重复使用地址(`SO_REUSEADDR`)、端口(`SO_REUSEPORT`)及超时时间(`SO_RCVTIMEO`)。其参数包括套接字描述符、协议级别、选项名称、选项值及其长度。成功返回0,失败返回-1并设置`errno`。示例展示了如何创建TCP服务器并设置相关选项。配套的`getsockopt()`函数用于获取这些选项的值。
305 12
|
Rust 安全 JavaScript
探索Rust在系统编程领域的前景:虚拟机和编译器开发的新篇章
【8月更文挑战第31天】在系统编程领域,性能与安全性至关重要。Rust作为一种新兴语言,凭借其独特的内存安全和并发特性,正逐渐成为虚拟机和编译器开发的首选。本文通过案例分析,探讨Rust在这些领域的应用,例如Facebook的Compiler VM (CVM)项目和实验性的JavaScript JIT编译器Mithril。Rust的静态类型系统和所有权模型确保了高性能和安全性,而其强大的包管理和库生态则简化了虚拟机的开发。随着Rust社区的不断成熟,预计未来将有更多基于Rust的创新项目涌现,推动系统编程的发展。对于追求高性能和安全性的开发者而言,掌握Rust将成为一个重要战略方向。
272 1
|
iOS开发 MacOS
macOS开机自启动执行脚本
【8月更文挑战第23天】在macOS上设置开机自动执行脚本可通过三种方式:一是利用“系统偏好设置”中的“用户与群组”功能直接添加脚本或应用;二是通过创建`.plist`文件并放置于`LaunchAgents`目录,这种方式能更精细地控制脚本运行;三是使用cron任务,在系统启动时执行脚本,但该方法不太适用于图形界面程序且可能受限于启动顺序。每种方法各有优缺点,需根据实际情况选择。
2187 0
|
存储 编译器 C语言
C语言之字符串与字符数组的区别
​ 1.字符串的定义: (1)单个字符: char ch='i';//单个字符的定义 (2)一维字符串数组: char arr[]="love";(这种方法定义的一维字符串数组必须赋值) char arr[4];(想内存申请创建可以存储3个字符的数组空间) char arr[5]=”love”;(开辟5个字节的空间存放字符love,最后一个字节存放'\0'字符) char arr[5]={'l','o',v','e'};(开辟5个字节的空间存放字符love,最后一个字节存放'\0'字符) 2.字符串长度 3.字符串和字符数组的区别: 由于C语言中没有string关键字,所以不能定义字符串
415 0
C语言之字符串与字符数组的区别
|
SQL 人工智能 Unix
AI如何影响数字化转型
AI如何影响数字化转型
|
算法 安全 架构师
浅浅瞅瞅RSA-PSS 算法
浅浅瞅瞅RSA-PSS 算法
642 0