首页 > 其他分享 >【博客】【入门级】递归

【博客】【入门级】递归

时间:2024-03-19 15:55:26浏览次数:15  
标签:return 数列 递归 int 博客 入门级 阶乘 排序

递归

有部分结论 代码 题目 来自网络

在文后有链接 侵删


前言

什么是递归

函数反复调用自身即是递归

举个栗子

递归

我在这篇博客里写了这篇博客的链接

像不像套娃

举个正经栗子

比如我们算\(n\)的阶乘\(f(n)\)

(阶乘就是\(1*2*3*4*...*n\))

以\(f(6)\)为例

\(f(6)\)

=> \(6\) *$ f(5)$

=> $ 6 $* \((5\) *$ f(4))$

=> \(6\) * \((5\) * \((4\) * \(f(3)))\)

=> \(6\) * \((5\) * \((4\) * \((3\) * \(f(2))))\)

=> \(6\) * \((5\) * \((4\) * \((3\) * \((2\) * \(f(1)))))\)

=> \(6\) * \((5\) * \((4\) * \((3\) * \((2\) * \(1))))\)

=> \(6\) * \((5\) * \((4\) * \((3\) * \(2)))\)

=> \(6\) * \((5\) * \((4\) * \(6))\)

=> \(6\) * \((5\) * \(24)\)

=> \(6\) * \(120\)

=> \(720\)

看到递归了吗?

代码写出来是这样的

#include<bits/stdc++.h>
using namespace std;
int get(int n)
{
    if(n==1)//回归
        return 1;
    else//递进
        return n*get(n-1);//函数自己调用了自己
}
int main()
{
    cout<<get(6)<<endl;
    return 0;
}

递进,再回归——这就是「递归」

通俗来讲 递归是一种懒人的甩锅思想

如果你自己计算不了

就甩给你的下级去做

我们不能成为这样的人

甩锅的过程就是函数调用自身

递归也可以看作是一个编程技巧

也是一种将规模大的问题转化为规模小的问题的思想


组成部分

明白了什么是递归之后

我们就要知道递归是怎么实现

递归由两部分组成

  1. 递归的终止条件

递归不能无终止的进行下去

一个老板把工作交给了经理

经理又交给了员工

员工总不能把工作给保洁阿姨吧

比如上面的例子

我们算到f(1)的时候就停止了

因为地球人都知道1的阶乘是1

我们总不能再去算0和-1的阶乘吧

如果我们再去不停的调用

程序就会死循环

所以递归的终止条件是十分重要的

if(n==1)
    return 1;

上面代码的这一句就是例题的终止条件

如果你写代码时不注意这一点

就会像下图一样看见满屏的错误

image

  1. 递归关系

这个就跟具体问题有关系了

还是之前的例子

我们要算6的阶乘 首先要知道5的阶乘

要算5的阶乘 首先要知道4的阶乘

要算i的阶乘 首先要知道i-1的阶乘

所以我们计算f(i)时 调用的是f(i-1)

再看一眼代码

#include<bits/stdc++.h>
using namespace std;
int get(int n)
{
    if(n==1)
        return 1;
    else
        return n*get(n-1);//函数自己调用了自己
}
int main()
{
    cout<<get(6)<<endl;
    return 0;
}

例题

1.兔子数列

兔子数列又称斐波那契数列

1,1,2,3,5,8,13...这样一个数列就是斐波那契数列,我们要求第n项的值。

观察数列可得

除了第一项和第二项,所有的数列的值都是前一项和前一项的前一项的和

转换成函数也就是f(n) = f(n-1) + f(n-2)

我们从递归的终止条件递归关系去入手

首先分析递归的终止条件

斐波那契数列的第1项和第2项是特殊的 都是1

所以我们算到第1项或第2项就可以返回了

也就是

if(n==1 || n==2)
    return 1;

然后再分析递归关系

斐波那契的第n项是第n-1项和第n-2项的和

我们要算第n项

就要知道第n-1项 和第n-2项

所以是这么调用的

else
    return f(n-1)+f(n-2);

总的代码是这样的

const int mod = 1000000007;

struct Matrix {
    int a[3][3];
    Matrix() { memset(a, 0, sizeof a); } // 构造函数,矩阵初始化全零
    Matrix operator*(const Matrix &b) const {
        Matrix res;
        for (int i = 1; i <= 2; ++i)
            for (int j = 1; j <= 2; ++j)
                for (int k = 1; k <= 2; ++k)
                    res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % mod;
        return res;
    }
} ans, base;

void init() { // 初始化 ans、base 矩阵
    base.a[1][1] = base.a[1][2] = base.a[2][1] = 1;
    ans.a[1][1] = ans.a[1][2] = 1;
}

void qpow(int b) { // 求
    while (b) {
        if (b & 1) ans = ans * base;
        base = base * base;
        b >>= 1;
    }
}

int main() {
    int n = read();
    if (n <= 2) return puts("1"), 0;
    init();
    qpow(n - 2);
    println(ans.a[1][1] % mod);
}

好像是矩阵快速幂 对不起 重来

只是想给大家整个乐子

下面是正经的

#include<bits/stdc++.h>
using namespace std;
int f(int n)
{
    if(n==1 || n==2)
    {
        return 1;
    }
    else
    {
        return f(n-1)+f(n-2);
    }
}
int main()
{
    int n;
    cin>>n;
    cout<<f(n)<<endl;
    return 0;
}

练习

1159 斐波那契数列

代码跟上面一样

1201 斐波那契数列

这道题多组数据

AC代码

#include<bits/stdc++.h>
using namespace std;
int f(int n)
{
    if(n==1 || n==2)
    {
        return 1;
    }
    else
    {
        return f(n-1)+f(n-2);
    }
}
int main()
{
    int n;
    cin>>n;
    while(n--)
    {
        int a;
        cin>>a;
        cout<<f(a)<<endl;
    }
    return 0;
}

1202 Pell数列

递归关系稍有变化\(f(n)=2*f(n-1)+f(n-2)\)

注意取模

不放代码了 自己回家慢慢做

加油


2.累加和

求\(1+2+3+......+n\)的结果

递归的终止条件是算到1的时候返回1

递归关系是\(f(n)=n+f(n-1)\)

代码如下

#include<bits/stdc++.h>
using namespace std;
int f(int n)
{
    if(n==1)
    {
        return 1;
    }
    else
    {
        return n+f(n-1);
    }
}
int main()
{
    int n;
    cin>>n;
    cout<<f(n)<<endl;
    return 0;
}

题目链接在这里

1158 求1+2+3+...


3.累乘积

求\(1*2*3*...*n\)的结果

这题是最开始的例子推广到了n

其实是一样的

也是把上一题的加改为乘

递归的终止条件是算到1的时候返回1

递归关系是\(f(n)=n*f(n-1)\)

#include<bits/stdc++.h>
using namespace std;
int f(int n)
{
    if(n==1)
    {
        return 1;
    }
    else
    {
        return n*f(n-1);
    }
}
int main()
{
    int n;
    cin>>n;
    cout<<f(n)<<endl;
    return 0;
}

接下来再说两个算法

快速排序归并排序

试图理解思想

不需理解代码

快速排序

快速排序是一个基于分治的排序方法

没学分治不要紧 明天有大佬wcj讲

给定一个数组a,该数组共有n个元素,我们需要对其进行从小到大的排序。

假设要排序的区间为\([ l , r ]\)

如果区间长度小于等于 1,那就直接退出(因为只有一个元素不用排序)

这就是递归的终止条件

否则在该区间中选择任意一个元素x作为基准元素。将大于x的元素放到x的右边;将小于x的元素放到x的左边

再对x的左右两边的区间分别进行递归即可。

这就是递归关系

我们看图模拟一下这个过程

图片来源

一文彻底搞懂快速排序算法

image

image

image

image

image

image

代码如下

能看到函数调用自身递归操作

void quick_sort(int l,int r){
    //区间 [l,r] 的元素个数为 r - l + 1
    //如果区间元素个数 <= 1 就直接返回,即 r - l + 1 <= 1
    //化简一下就是 l >= r
    if(l >= r) return;
    swap(a[l] , a[l + rand() % (r - l + 1)]);//随机选择元素
    int x = a[l];
    int i = l ,j = r;
    while(i < j){
        //先从右往左找到第一个 <= x 的元素
        while(i < j && a[j] > x) j--;
        //把找到的元素放到位置 i 上
        if(i < j) a[i++] = a[j];
        //再从左往右找到第一个 >= x 的元素
        while(i < j && a[i] < x) i++;
         //把找到的元素放到位置 j 上
        if(i < j) a[j--] = a[i];
    }
    //最后,i 和 j 同时指向了同一个位置 p
    //我们就把 a[p] 的值赋为 x
    //x 的位置就已经确定了
    a[i] = x;
    //再递归的处理剩下的两个区间,即 [l , p - 1] 和 [p + 1 , r]
    quick_sort(l,i-1);
    quick_sort(i+1,r);
}

归并排序

归并排序,是创建在归并操作上的一种有效的排序算法。

归就是层层递归将数列分开

并就是把排序好的左右数列合并

合并时每次选择左右序列比较小的那个元素

看图理解一下

图片来源

【算法】排序算法之归并排序

image

上图中首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据(递归的终止条件),再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列

代码如下

同样能看到函数调用自身递归操作

void _MergeSort(int* arr, int begin, int end, int* tmp)
{
	if (begin >= end)
	{
		return;
	}
	//分治递归,让子区间先有序
	int mid = (begin + end) / 2;
	_MergeSort(arr, begin, mid, tmp);
	_MergeSort(arr, mid + 1, end, tmp);
	
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;
	int i = begin1;
	//在arr[begin1,end1]和arr[begin2,end2]两个区间中开始归并
	while ((begin1 <= end1) && (begin2 <= end2))
	{
		//如果arr[begin1]的值小,就让arr[begin1]的值先进入tmp数组
		if (arr[begin1] < arr[begin2])
		{
			tmp[i++] = arr[begin1++];
		}
		//如果arr[begin2]的值小于等于arr[begin1],就让arr[begin2]的值进入tmp数组
		else
		{
			tmp[i++] = arr[begin2++];
		}
	}
	//如果数组1中的数据还没有归并到tmp中完,就直接将剩下的都放到tmp中
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	//如果数组2中的数据还没有归并到tmp中完,就直接将剩下的都放到tmp中
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	//把归并数据拷贝到原数据
	memcpy(arr + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* arr, int n)
{
	//归并排序需要先申请一个辅助数组
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("MergeSort malloc fail");
		exit(-1);
	}
	_MergeSort(arr, 0, n - 1, tmp);
}

到此递归就讲完了

其实是讲不完的

因为递归很常用

学是学不完的

之后的无数算法要用到递归思想


引用来源

全面理解递归 - 知乎 (zhihu.com)

一文弄懂递归 - 知乎 (zhihu.com)

快速排序详解-CSDN博客

「一发入魂」一文彻底搞懂快速排序算法 (baidu.com)

【算法】排序算法之归并排序 - 知乎 (zhihu.com)

排序之归并排序-CSDN博客

只供学习使用 侵删

标签:return,数列,递归,int,博客,入门级,阶乘,排序
From: https://www.cnblogs.com/zysssss/p/18083128

相关文章

  • 新电脑 个人博客迁移
    安装和配置所需要的软件安装Git客户端,安装过程省略,一般默认下一步    下载地址:Git客户端    这个无脑下一步即可无需配置安装nodeJS,安装过程省略,一般默认下一步    下载地址:nodeJS    配置看这位大佬教程:地址拷贝个人博客文件夹中,部......
  • 第七节:二叉树的先序、中序、后续遍历的多种递归写法
    一.        二.        三.         !作       者:Yaopengfei(姚鹏飞)博客地址:http://www.cnblogs.com/yaopengfei/声     明1:如有错误,欢迎讨论,请勿谩骂^_^。声     明2:原创博客请在转载......
  • 二叉树|二叉树理论基础、二叉树的递归遍历
    代码随想录(programmercarl.com)树和二叉树1.树的基本概念1.1树的定义1.2树的逻辑表示方法1.3树的基本术语1.4树的性质1.5树的基本运算1.6树的存储结构2.二叉树的概念和性质2.1二叉树的定义2.2二叉树的性质2.3二叉树与树、森林之间的转换3.二叉树的存储结构3.1......
  • Python 递归函数实现二分法,带思路解释
            二分法可以大大提升对有序数列的查找,传统的迭代查找会挨个比较数列中的值,如果数列较为庞大会影响查询效率。二分法每次取数列的中间数与待查找数字比较大小,以升序排列为例子 首先要考虑数列长度的奇偶性。        奇数取中间位置的数字,如果比待查找......
  • CNN入门级实战教程
    本教程将使用Keras构建一个简单的的卷积神经网络(ConvolutionalNeuralNetwork,CNN)来对手写数字进行识别。使用的数据集为MNIST数据集,一个包含手写数字图像的经典数据集。0.环境设置确保你已经安装了所需的库,可以通过以下方式安装:pipinstallkerascondainstallmatplotl......
  • 将博客搬至CSDN
    New·Object将博客搬至CSDN在数字时代的浪潮中,我始终如一地维护着自己的一方网络空间。从最初的个人网站到现在的CSDN博客,每一次的变迁都记录着我的成长与变化。今天,我想和大家分享一下将博客搬迁至CSDN的决定背后的故事,以及这个决定给我带来的一系列连锁反应。记得最初,我的博......
  • 递归组件实现子向父传值
    业务逻辑:通过自己调用自己的方式生成树,再点击子菜单时,需要将点击子菜单的菜单名传值给父组件(使用总线bus) 新建bus.js文件import{ref}from'vue'classBus{constructor(){//收集订阅信息,调度中心this.eventList={},//事件列表,这项是必须的//......
  • 一.函数的递归
    简单而通俗易懂的说,函数的递归就是:函数自己调用自己。就是把大事化小事,递的意思就是推进的意思。归就是回归的意思。递归的限制:两个条件:1.递归存在限制条件,当满⾜这个限制条件的时候,递归便不再继续。                        ......
  • 递归示例-展开编号(Excel函数集团)
    展开编号=DROP(fx(COUNTA(B:B)-1),1)fx=LAMBDA(x,IF(x>0,VSTACK(fx(x-1),SEQUENCE(INDEX(Sheet4!$B:$B,x+1),,INDEX(Sheet4!$C:$C,x+1)))))使用Lambda定义x当x小于等0时,返回False,以此作为开关;当x为1时,返回False连接SEQUENCE(INDEX(Sheet4!$B:$B,2),,INDEX(Sheet4!$C:......
  • 数据结构笔记(十四)二叉树的遍历(递归)
    四种访问方式:前序遍历,中序遍历,后序遍历,层序遍历这篇文章主要为前序,中序,后序遍历的递归形式,递归形式较为简单,后面更新遍历的循环形式较为复杂,建议使用递归形式#include<stdio.h>#include<stdlib.h>typedefcharE;typedefstructTreeNode*Node;structTreeNod......