数据结构与算法(1):递归函数的设计技巧

news2024/9/21 14:40:34

1.前言

哈喽小伙伴们大家好哦~从今天开始笔者就要开始正式学习数据结构与算法了,在这里写知识博客既是做一些学习笔记,又相当于给大家做知识分享咯,希望大家一起加油哦!

2.正文

2.1递归的引入

在正式讲解递归之前,我们需要先介绍一个数学的知识——数学归纳法。大家可能会疑惑,数学归纳法和本文讲解的利用递归实现函数功能解决实际问题有什么关系呢?往下看即可:

2.1.1数学归纳法

数学归纳法是一种数学证明方法,主要用于证明某个给定命题在整个(或者局部)自然数范围内成立。


数学归纳法的原理在于:

  1. 首先证明在某个初始值(通常情况下是参数n取1,但也不是必须的)时命题成立。
  2. 然后证明可以从任意一个值的成立推导出下一个值也成立。当这两点都已经证明,就可以通过反复使用这个方法验证对所有的正整数都成立。

具体步骤包括:

  1. 验证n取第一个自然数时命题成立。
  2. 假设n=k时命题成立,然后以验证的条件和假设的条件作为论证的依据进行推导,证明当n=k+1时命题也成立(k代表任意自然数)。(在这个过程中,不能直接将n=k+1代入假设的原式中去这是常犯的错误之一)

示例:

  1. 证明1+2+3+...+n=n×(n+1)/2。首先,当n=1时,显然成立。
  2. 然后,假设当n=k时命题成立,即1+2+3+...+k=k×(k+1)/2,那么当n=k+1时,1+2+3+...+k+(k+1)=k×(k+1)/2+(k+1)=(k+1)×(k+2)/2,也成立。
  3. 依次递推,命题对于任意n都成立。

2.1.3数学归纳法与编程

#include<stdio.h>

int main() {
	int sum = 0;
	for (int i = 1; i <= 100; i++) {
		sum += i;
	}
	printf("%d", sum);
	return 0;
}

我们按照上文的步骤来进行梳理:(虽然这段代码的思路和执行都一目了然,但是我们可以通过它很好的说明白问题)

  1. 当i=1时,sum等于1符合题意。
  2. 假设i-1成立,我们用f(i-1)来表示相加的总和,则显然f(i)=f(i-1)+i(i大于1小于等于100时),得证。
  3. 依次递推,该结构逻辑严密

由此我们便完成了数学归纳法在编程上的思想体现。

2.1.3数学归纳法与递归

递归基本原理是通过直接或间接地调用自身算法的过程来解决问题。递归算法通常包括一个或多个基本情况(即不需要递归就能直接求解的情况)和一个或多个递归情况(即需要调用自身算法来求解的情况)。


而根据我们上文讲述的数学归纳法,都利用了问题的逐步分解和递推求解的思路。我们都通过对问题的逐步分解和递推求解来解决更小的子问题。


由此,我们在利用递归思想建立函数时就可以采用这样的分析方式来进行分析。

  1. 递归函数一个明确的语义(要明确是要做什么的)
  2. 实现边界条件的程序逻辑
  3. 设递归函数用返回果是正确的,实现函数逻辑(逐步分解,最终变成一个小问题)

2.2递归的简单实现

2.2.1路飞吃桃


题目描述

路飞买了一堆桃子不知道个数,第一天吃了一半的桃子,还不过瘾,又多吃了一个。以后他每天吃剩下的桃子的一半还多一个,到 n 天只剩下一个桃子了。路飞想知道一开始买了多少桃子。

输入描述

输入一个整数 n(2≤n≤30)。

输出描述

输出买的桃子的数量。

示例


代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

int n = 0;

int solve(int x) {
	if (x == 1)return 1;
	else return 2 * (solve(x - 1) + 1);
}

int main() {
	cin >> n;
	int ans = solve(n);
	cout << ans << endl;
	return 0;
}

这道题虽然很基础,但却将递归的简单实现体现的淋漓尽致,具体不详解。 

2.2.2弹簧板


题目描述

有一个小球掉落在一串连续的弹簧板上,小球落到某一个弹簧板后,会被弹到某一个地点,直到小球被弹到弹簧板以外的地方。

假设有 n 个连续的弹簧板,每个弹簧板占一个单位距离,a[i] 代表代表第 i 个弹簧板会把小球向前弹 a[i] 个距离。比如位置 1 的弹簧能让小球前进 2 个距离到达位置 3 。如果小球落到某个弹簧板后,经过一系列弹跳会被弹出弹簧板,那么小球就能从这个弹簧板弹出来。

现在小球掉到了1 号弹簧板上面,那么这个小球会被弹起多少次,才会弹出弹簧板。 1号弹簧板也算一次。

输入描述

第一个行输入一个 n 代表一共有 n(1≤n≤100000)个弹簧板。

第二行输入 n​ 个数字,中间用空格分开。第 i​ 个数字 a[i](0<a[i]≤30)​ 代表第 i​ 个弹簧板可以让小球移动的距离。

输出描述

输出一个整数,表示小球被弹起的次数。

示例


代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;

const int N = 1e6;
int a[N];
int n,ans;

int solve(int x, int a[N],int n) {
	if (x >= n)return 0;
	else return solve(x + a[x], a, n) + 1;
}

int main() {
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	ans = solve(0, a, n);
	cout << ans << endl;
	return 0;
}

 当然这里可以用vector容器简单改进一下,可以降低空间复杂度。

vector和普通数组的区别:
1.数组是静态的,长度不可改变,而vector可以动态扩展,增加长度
2.数组内数据通常存储在栈上,而vector中数据存储在堆上


push_back函数是用于在vector容器末端添加一个数

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;

const int N = 1e6;
vector <int>a;
int n=0,ans;

int solve(int x, vector <int>&a, int n){
	if (x >= n)return 0;
	else return solve(x + a[x], a, n) + 1;
}

int main() {
	cin >> n;
	for (int i = 0, m; i < n; i++) {
		cin >> m;
		a.push_back(m);
	}
	ans = solve(0, a, n);
	cout << ans << endl;
	return 0;
}

2.2.3递归实现指数型枚举


题目描述

​ 从 1−n 这 n 个整数中随机选取任意多个,每种方案里的数从小到大排列,按字典序输出所有可能的选择方案。

输入描述

 输入一个整数 n。(1≤n≤10)

输出描述

​ 每行一组方案,每组方案中两个数之间用空格分隔。

​ 注意每行最后一个数后没有空格。

示例


 在正式做提前,先弄清楚一个概念,何为字典序?

字典序是一个按照字典顺序排列元素的方式。在字典序中,元素按照它们在字典中的顺序进行排序,即按照字符或数字的自然顺序排列。

在字符串中,字典序就是按照字母表的顺序来排列字符串。如果两个字符串长度不同,较短的字符串会在较长的字符串之前。如果两个字符串的前几个字符相同,那么它们将按照下一个字符的顺序进行排序,以此类推。

代码
#include <iostream>
using namespace std;

int arr[10];

void func(int n) {
    for (int i = 0; i <= n; i++) {
        if (i) cout << " ";
        cout << arr[i];
    }
    cout << endl;
    return ;
}

void f(int i, int j, int n) {
    if (j > n) return ;
    for (int k = j; k <= n; k++) {
        arr[i] = k;
        func(i);
        f(i + 1, k + 1, n);
    }
    return ;
}
int main() {
    int n;
    cin >> n;
    f(0, 1, n);//分别用来记录位置,所填入的数字,以及最大数
    return 0;
}

2.2.4递归实现组合型枚举


题目描述

从 1−n 这 n 个整数中随机选取 m 个,每种方案里的数从小到大排列,按字典序输出所有可能的选择方案。

输入描述

输入两个整数 n,m。(1≤m≤n≤10)

输出描述

​ 每行一组方案,每组方案中两个数之间用空格分隔。

​ 注意每行最后一个数后没有空格。

示例


代码
#include <iostream>
using namespace std;

int arr[10];

void print_one_result(int n) {
    for (int i = 0; i < n; i++) {
        if (i) cout << " ";
        cout << arr[i];//解决空格问题
    }
    cout << endl;
    return;
}

void f(int i, int j, int n, int m) {
    if (i == m) {
        print_one_result(m);//单独创建打印函数
        return;
    }
    //俩个判断条件一个是不要超过指定数量,另一个是后面剩下的数字要大于等于还需要的数字
    for (int k = j; k <= n && m - i - 1 <= n - k; k++){
        arr[i] = k;
        f(i + 1, k + 1, n, m);//核心递归部分
    }
    return;
}

int main() {
    int n, m;
    cin >> n >> m;
    f(0, 1, n, m);//分别记录最小数,最大数,指定的枚举最大值以及要枚举多少位
    return 0;
}

2.2.5递归实现排列型枚举


题目描述

从 1−n 这 n 个整数排成一排并打乱次序,按字典序输出所有可能的选择方案。

输入描述

输入一个整数 n。(1≤n≤8)

输出描述

​ 每行一组方案,每组方案中两个数之间用空格分隔。

​ 注意每行最后一个数后没有空格。

示例


代码
#include <iostream>
using namespace std;

int arr[10], vis[10] = { 0 };

void print_one_result(int n) {
    for (int i = 0; i < n; i++) {
        if (i) cout << " ";
        cout << arr[i];
    }
    cout << endl;
    return;
}

void f(int i, int n) {
    if (i == n) {
        print_one_result(n);
        return;
    }
    for (int k = 1; k <= n; k++) {
        if (vis[k]) continue;
        arr[i] = k;
        vis[k] = 1;
        f(i + 1, n);
        vis[k] = 0;
    }
    return;
}

int main() {
    int n;
    cin >> n;
    f(0, n);
    return 0;
}

3.小结

今天数据结构第一讲:递归函数的设计技巧到这里就结束了,希望喜欢的朋友多多支持我哦~(敲完了以后脑子快爆炸了)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1927692.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

创建鸿蒙手机模拟器(HarmonyOS Emulator)

文 | Promise Sun 一.前提条件&#xff1a; 鸿蒙项目开发需要使用模拟器进行开发测试&#xff0c;但目前想在DevEco Studio开发工具中使用模拟器就必须到华为官网进行报名申请&#xff0c;参加“鸿蒙模拟器&#xff08;HarmonyOS Emulator&#xff09;Beta活动申请”。 申请审…

中间件的理解

内容来源于学习网站整理。【一看就会】什么是前端开发的中间件&#xff1f;_哔哩哔哩_bilibili 每日八股文~白话说mq&#xff0c;消息中间件_哔哩哔哩_bilibili 例如&#xff1a; 1&#xff09;两个人打电话&#xff0c;中间的通信网络就是中间件。 2&#xff09;菜鸟驿站&…

SpringBoot以及swagger的基本使用

1、SpringBoot是什么&#xff1f; 一种快速开发、启动Spring的框架、脚手架 遵循“约定优于配置”的思想&#xff0c;使得能够快速创建和配置Spring应用 2、SpringBoot的核心特性 自动配置&#xff0c;一些依赖、默认配置都预设好了&#xff0c;减少了配置量起步依赖&#x…

ROS2-Navigation2初体验:Gazebo“打不开”

输入ros2 launch nav2_bringup tb3_simulation_launch.py headless:False后只能打开RVIZ而无法打开Gazebo的问题&#xff0c;多次尝试解决后发现只是多等待一会儿即可&#xff0c;在此给同样学习Navigation2的朋友们提个醒 。 Getting Started — Nav2 1.0.0 documentation 1…

Mindspore框架CycleGAN模型实现图像风格迁移|(二)实例数据集(苹果2橘子)

Mindspore框架&#xff1a;CycleGAN模型实现图像风格迁移算法 Mindspore框架CycleGAN模型实现图像风格迁移|&#xff08;一&#xff09;CycleGAN神经网络模型构建Mindspore框架CycleGAN模型实现图像风格迁移|&#xff08;二&#xff09;实例数据集&#xff08;苹果2橘子&#…

补充性文件

第一 二章 1&#xff0c;关系型数据库是什么&#xff1f;其中的关系是指什么&#xff1f; 答&#xff1a; 关系型数据库是一些相关的表和其他数据库对象的集合。数据模型符合满足一定条件的二维表格式。 2&#xff0c;E-R模型&#xff1f; 实体为表。用矩形表示。属性为字…

嵌入式物联网在工业中的应用——案例分析

作者主页: 知孤云出岫 目录 嵌入式物联网在工业中的应用——案例分析引言1. 智能工厂1.1 实时监控与数据采集 2. 智能物流2.1 库存管理 3. 智能维护3.1 设备故障预测 4. 智能交通4.1 交通流量监测 总结 嵌入式物联网在工业中的应用——案例分析 引言 嵌入式物联网&#xff08;…

回车不搜索直接页面刷新问题解决

使用技术栈&#xff1a;vue3、elementUiPlus 问题&#xff1a;回车触发方法&#xff0c;会刷新整个页面&#xff0c;不执行搜索 解决方法&#xff1a;在搜索的表单中增加submit.native.prevent submit.native.prevent

项目管理:不懂跟进,项目白做

在职场上&#xff0c;工作的本质其实就是信息的传递与处理。而信息的及时传递&#xff0c;也就是我们常说的及时跟进&#xff0c;往往被许多项目经理和职场人忽视。 他们或许在暗地里埋头苦干&#xff0c;却忽略了明面上的沟通与汇报&#xff0c;最终导致合作方和内部团队都对…

利用AI辅助制作ppt封面

如何利用AI辅助制作一个炫酷的PPT封面 标题使用镂空字背景替换为动态视频 标题使用镂空字 1.首先&#xff0c;新建一个空白的ppt页面&#xff0c;插入一张你认为符合主题的图片&#xff0c;占满整个可视页面。 2.其次&#xff0c;插入一个矩形&#xff0c;右键选择设置形状格式…

【SpringBoot】SpringCache轻松启用Redis缓存

目录&#xff1a; 1.前言 2.常用注解 3.启用缓存 1.前言 Spring Cache是Spring提供的一种缓存抽象机制&#xff0c;旨在通过简化缓存操作来提高系统性能和响应速度。Spring Cache可以将方法的返回值缓存起来&#xff0c;当下次调用方法时如果从缓存中查询到了数据&#xf…

JDK,JRE,JVM三者之间的关系

Java程序不是直接在操作系统之上运行&#xff0c;而是运行在JVM&#xff08;java虚拟机&#xff09;之上。 Java源代码&#xff08;.java文件&#xff09;经编译器编译成字节码&#xff08;.class文件&#xff09;&#xff0c;JVM本质上就是一个负责解释执行Java字节码的程序。…

结合实体类型信息(2)——基于本体的知识图谱补全深度学习方法

1 引言 1.1 问题 目前KGC和KGE提案的两个主要缺点是:(1)它们没有利用本体信息;(二)对训练时未见的事实和新鲜事物不能预测的。 1.2 解决方案 一种新的知识图嵌入初始化方法。 1.3 结合的信息 知识库中的实体向量表示&#xff0b;编码后的本体信息——>增强 KGC 2基…

鸿蒙跨平台框架ArkUI-X 小试牛刀视频播放

团队介绍 作者:徐庆 团队:坚果派 公众号:“大前端之旅” 润开鸿生态技术专家,华为HDE,CSDN博客专家,CSDN超级个体,CSDN特邀嘉宾,InfoQ签约作者,OpenHarmony布道师,电子发烧友专家博客,51CTO博客专家,擅长HarmonyOS/OpenHarmony应用开发、熟悉服务卡片开发。欢迎合…

桥接器设计模式例题

笔有大、中、小三种型号&#xff0c;纸有A4、8K、16K三种型号&#xff0c;颜料有红、蓝、绿三种&#xff0c;请采用桥接器设计模型进行系统设计&#xff0c;能够使用不同型号的笔在不同型号的纸上利用不同颜色的颜料进行绘画。 下面这段代码展示了一个简单的桥接模式(桥接模式)…

数据结构之初始二叉树(2)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 二叉树的前置知识&#xff08;概念、性质、、遍历&#xff09; 通过上篇文章的学习&#xff0c;我们…

Babylonjs学习笔记(十一)——加载geoJson文件

一、定义基本场景类 定义场景定义相机 import { ArcRotateCamera, Color4, CubeTexture, Engine, GlowLayer, KeyboardEventTypes, Scene, Vector3 } from babylonjs/core;import { AdvancedDynamicTexture } from babylonjs/gui;class SceneManager {public engine: Engine;…

springboot系列教程(一):简介与入门案例(含源码)

一、SpringBoot简介 SpringBoot继承了Spring优秀的基因&#xff0c;上手难度小简化配置&#xff0c;提供各种默认配置来简化项目配置内嵌式容器简化Web项目&#xff0c;简化编码 Spring Boot 则会帮助开发着快速启动一个 web 容器&#xff0c;在 Spring Boot 中&#xff0c;只…

【Linux】从零开始认识多线程 --- 线程控制

在这个浮躁的时代 只有自律的人才能脱颖而出 -- 《觉醒年代》 从零开始认识多线程 --- 线程控制 1 知识回顾2 线程控制2.1 线程创建2.2 线程等待2.3 线程终止 3 测试运行3.1 小试牛刀 --- 创建线程3.2 探幽析微 --- 理解线程参数3.3 小有心得 --- 探索线程返回3.4 求索无厌 …

金九银十,软件测试面试题大全(自动化篇+含答案)

“ 今天我给大家介绍一些python自动化测试中常见的面试题&#xff0c;涵盖了Python基础、测试框架、测试工具、测试方法等方面的内容&#xff0c;希望能够帮助你提升自己的水平和信心。” 项目相关 1.什么项目适合做自动化测试&#xff1f; 答&#xff1a;一般来说&#xff…