递归和分治(基础)

news2024/9/28 9:25:31

目录

一、递归的定义

1、什么时候会用到递归的方法

1. 定义是递归的

2. 数据结构是递归的

3. 问题的解法是递归的 

2、应用递归的原则

3、递归调用顺序问题 

1. 首先递归的过程可以总结为以下几点:

2. 递归工作栈

二、 递归和非递归的转化

1. 单向递归可用迭代 

举例:斐波拉契数列

2. 尾递归可用迭代

举例:欧几里德求最大公约数

3. 借助栈实现非递归

1. 举例:二叉树的先序遍历

2. 举例:二叉树的中序遍历

三、分治解题步骤

四、汉诺塔问题

1. 问题

2. 解题思路 

3. 两个盘子演示

4. 三个盘子演示

5. 四个盘子演示 

6. 代码


一、递归的定义

  • 在数学及程序设计方法学中为递归下的定义是这样的:若一个对象部分包含地包含它自己,或用它自己来定义它自己,则称这个对象是递归的;若一个过程直接或间接的调用自己,则称这个过程为递归的过程。
  • 简而言之,递归方法就是直接或间接地调用其自身

1、什么时候会用到递归的方法

1. 定义是递归的

  • 以数学上常用的阶乘函数为例,其定义和计算都是递归的

定义:

\large n!=\begin{cases}1 & x = 0\\n(n-1)! & x > 0\end{cases}

 求解函数:

int faction(int n)
{
	if (n == 1)
		return 1;
	else
		n = faction(n - 1) * n;
	return n;

}

2. 数据结构是递归的

  • 链表就是一种递归的数据结构,从概念上讲,单链表可以递归的定义为一个结点,当该结点的指针域为NULL的时候,就表明此链表是一个单链表,这个结点的指针域也可以指向另一个单链表,而这个单链表具有同样的结构
  • 也可以采用递归的方式来描述。首先一棵树要么是空,要么由若干非空子树组成(子树的数目可以为空),且这些子树的根都通过一条边连到根上。每个子树同样具有这样的结构,要么为空,要么由根和若干非空子树组成。

3. 问题的解法是递归的 

2、应用递归的原则

  • 首先是必须要有一些“基本条件”能够采用非递归的方式计算得到,这是使用递归方法的重要前提。基本条件的满足意味着采用递归处理后的子问题可以直接解决时,就停止分解,而这些可以直接求解的问题就叫做递归的“基本条件”。
  • 为了使计算最终完结,任何递归调用都要朝着“基本条件”的方向进行。
  • 例1:前面所举例的阶乘
if (n == 1)
   return 1;

 这就是所谓的基本条件。

  • 例2:二分查找(折半查找) 
int BinSearch(int* data, int key,int low,int high)//折半查找
{
	if (low > high)
		return -1;
	int mid = (low + high) / 2;
	//二分查找递归的核心部分
	if (key < data[mid])
		return(data, key, low, mid - 1);//继续在data[low,mid-1]左区间查找
	else if (key > data[mid])
		return(data, key, mid + 1, high);//继续在data[mid+1,high]右区间查找
	else
		return mid;//查找成功
}
  • 其中:if(low>high)是基本条件之一,即在搜索范围内无法找到想要查找的值,表示搜索失败,递归过程也就结束了;此外,找到想要查找的值递归过程也会结束,也就是“return mid”,其对应的条件是“if(key==data[mid]”。

3、递归调用顺序问题 

1. 首先递归的过程可以总结为以下几点:

  • 递归过程在实现时,需要自己调用自己
  • 层层向下递归,退出次序正好相反
  • 主程序第一次递归调用自己为内部调用
  • 它们返回调用它的过程的地址不同

2. 递归工作栈

  • 在递归过程中,递归的执行需要一些薄记空间来记录跟踪前一个递归调用,特别对于那些有一长串递归调用的情况,在某种程度上较同等循环而言更加费时,因为薄记工作本身就要消耗一定时间。这个薄记空间就是递归工作栈
  • 同时,每一次递归调用时,需要为过程中使用的参数、局部变量等另外分配存储空间。
  • 每层递归调用需分配函数递归时的活动记录 可以用如下图表示:

 

  •  以下列代码为例:
#include<iostream>
using namespace std;
void Fuction1(int n)
{
	if (n < 4)
	{
		printf("%d\n", n);
		Fuction1(n + 1);
	}
}
void Fuction2(int n)
{
	if (n < 4)
	{
		Fuction2(n + 1);
		printf("%d\n", n);
	}
}
int main()
{
	cout << "第一个函数:" << endl;
	Fuction1(0);
	cout << "第二个函数:" << endl;
	Fuction2(0);
	return 0;
}
  • Fuction1函数的执行过程

  • Fuction2函数执行过程:

二、 递归和非递归的转化

1. 单向递归可用迭代 

  • 单向递归: 是指递归的过程总是朝着一个方向进行。斐波拉契数列的求解就是单向递归
  • 举例:斐波拉契数列

  • 递归求解斐波拉契数列: 
int Fib(int n)
{
	if (n < 2)
		return n == 0 ? 0 : 1;
	else if (n >= 2)
	{
		n = Fib(n - 1) + Fib(n - 2);
		return n;
	}
}
  •  迭代求解斐波拉契数列:
int Fib(int n)
{
	vector<int> v;
	v.push_back(0);
	v.push_back(1);
	int i = 2;
	for (i = 2; i <= n; i++)
	{
		int t = v[i - 1] + v[i - 2];
		v.push_back(t);
	}
	return v[n];
}

2. 尾递归可用迭代

  • 尾递归函数是以递归调用作为结尾的函数,它是单向递归的特例,它的递归调用语句只有一个,而且放在过程最后。当递归调用返回时,返回到上一层递归调用语句的下一语句的时候,而这个位置正好是程序的结尾。
  • 尾递归示意图:

  • 举例:欧几里德求最大公约数

  • 是用较大的数除以较小的数,较小的除数和得出的余数构成新的一对数,继续做上面的除法,直到出现能够整除的两个数。
  •  递归求解最大公约数:
int gcd(int a, int b)
{
	if (b == 0)
		return a;
	else
		gcd(b, a % b);
}
  • 迭代求解最大公约数:
int gcd(int a, int b)
{
	int tmp;//保存a%b
	while (b!=0)
	{
		tmp = a % b;
		a = b;
		b = tmp;
	}
	return a;
}

3. 借助栈实现非递归

1. 举例:二叉树的先序遍历

  • 递归先序遍历:
void PreOrder(BTree T)//先序遍历
{
    if (T != NULL)
    {
        cout << T->data << " ";//访问根结点
        PreOrder(T->lchild);//先序遍历左子树
        PreOrder(T->rchild);//先序遍历右子树
    }
}
  • 用栈先序遍历:
bool First(BTree T)
{
    stack<BTNode*>s;
    BTNode* p = T;
    if (p != NULL )//二叉树不为空
    {
        s.push(p);
        while (!s.empty())//栈不为空
        {
            p = s.top();
            cout << s.top()->data << " ";//先访问栈顶元素
            s.pop();//栈顶元素退栈
            if (p->lchild != NULL)
                s.push(p->rchild);//栈顶元素的右孩子结点进栈
            if (p->rchild != NULL)
                s.push(p->lchild);//栈顶元素的左孩子结点进栈
        }
    }
    return true;
}

2. 举例:二叉树的中序遍历

  • 递归中序遍历:
void InOrder(BTree T)//中序遍历
{
    if (T != NULL)
    {
        InOrder(T->lchild);
        cout << T->data << " ";
        InOrder(T->rchild);
    }
}
  • 用栈中序遍历:
bool InOder(BTree T)
{
    stack<BTNode*>s;
    BTNode* p = T;
    while (p != NULL || !s.empty())
    {
        while (p != NULL)//当前结点不为空
        {
            s.push(p);
            p = p->lchild;
        }
        if (!s.empty())
        {
            cout << s.top()->data << " ";//访问栈顶元素
            p = s.top()->rchild;//先将栈顶元素的右孩子存储起来
            s.pop();//栈顶元素出栈
        }
    }
    return true;
}

三、分治解题步骤

1. 分解:将要解决的问题分解为若干个规模较小、相互独立、与原问题形式相同的子问题

2. 治理:求解各个子问题。由于各个子问题与原问题形式相同,只是规模较小而已,而当子问题划分得足够小时,就可以用较简单的方法解决

3. 合并:按原问题的要求,将子问题的解逐层合并构成原问题的解 

  •  解决方案示意图:

四、汉诺塔问题

1. 问题

  • 在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
  1. 每次只能移动一个盘子;
  2. 盘子只能从柱子顶端滑出移到下一根柱子;
  3. 盘子只能叠在比它大的盘子上。
  • 将所有盘子从第一根柱子移到最后一根柱子 

2. 解题思路 

  • 当n=1(盘子数目为1)的时候,只需将盘子从A移到C即可
  • 当n>1(盘子数目大于1)的时候,利用B作为辅助,将n-1个较小的盘子从A移到B,再将剩余的一个盘子从A移动到C。最后再将n-1个小盘从B依次移动到A。

3. 两个盘子演示

 

4. 三个盘子演示

 

5. 四个盘子演示 

 

 

6. 代码

#include<iostream>
using namespace std;
void Move(int n, char x, char y)
{
	cout << "将编号为" << n << "的盘子从" << x << "移向" << y << endl;
}
void hanota(int n,char a,char b,char c)
{
	if (n == 1)
		Move(1, a, c);
	else
	{
		hanota(n - 1, a, c, b);//将n-1个盘子从a移到b
		Move(n, a, c);//将剩下的第n个盘子直接移到c处
		hanota(n - 1, b, a, c);//又将n-1个盘子从b移到c
	}
}
int main()
{
	hanota(3, 'A', 'B', 'C');
	return 0;
}

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

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

相关文章

【JavaGuide面试总结】计算机网络·下

【JavaGuide面试总结】计算机网络下1.HTTP 和 HTTPS 有什么区别&#xff1f;2.HTTP 1.0 和 HTTP 1.1 有什么区别&#xff1f;连接方式状态响应码缓存处理Host头处理带宽优化3.HTTP 是不保存状态的协议, 如何保存用户状态?4.简单说说 ARP 协议的工作原理同一局域网内的 MAC 寻址…

【Java寒假打卡】JavaWeb-ServletContext

【Java寒假打卡】JavaWeb-ServletContext概述域对象ServletContext的配置方式ServletContext的常用方法ServletContext共享数据的方法概述 ServletContext是应用上下文对象&#xff08;应用域对象&#xff09;。每一个应用中只有一个ServletContext对象作用&#xff1a;可以配…

kubernetes集群搭建问题记录

centos7 系统 内核需要升级 centos7内核升级文章 init-config.yaml文件初始化master的时候 advertiseAddress 是主机的ip地址 kubeadm kubelet kubectl 安装 1.19.0 版本&#xff0c;版本高了有问题 yum install -y kubeadm-1.19.0 kubelet-1.19.0 kubectl-1.19.0 master 和…

<Python的字典>——《Python》

目录 1. 字典 1.1 字典是什么 1.2 创建字典 1.3 查找 key 1.4 新增/修改元素 1.5 删除元素 1.6 遍历字典元素 1.7 取出所有 key 和 value 1.8 合法的 key 类型 1. 字典 1.1 字典是什么 字典是一种存储 键值对 的结构. 键值对是计算机/生活中一个非常广泛使用的概念…

【C++算法图解专栏】一篇文章带你掌握前缀和算法(一维+二维)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4e3;专栏定位&#xff1a;为 0 基础刚入门数据结构与算法的小伙伴提供详细的讲解&#xff0c;也欢迎大佬们一起交流~ &#x1f4da;专栏地址&#xff1a;https://blog.csdn.net/Newin…

Leetcode.1824 最少侧跳次数

题目链接 Leetcode.1824 最少侧跳次数 题目描述 给你一个长度为 n的 3 跑道道路 &#xff0c;它总共包含 n 1个 点 &#xff0c;编号为 0 到 n 。一只青蛙从 0 号点第二条跑道 出发 &#xff0c;它想要跳到点 n处。然而道路上可能有一些障碍。 给你一个长度为 n 1的数组 ob…

ESP32设备驱动-DHT22数字温度湿度传感器驱动

DHT22数字温度湿度传感器驱动 1、DHT22介绍 DHT22电容式湿度传感数字温湿度模块是一款包含复合已校准数字信号输出的温湿度传感器。 应用了专用的数字模块采集技术和温湿度传感技术,确保产品具有高可靠性和优异的长期稳定性。 该传感器包括一个电容式传感器湿元件和一个高精…

Word2Vec与文章相似度

2.7 Word2Vec与文章相似度 学习目标 目标 知道文章向量计算方式了解Word2Vec模型原理知道文章相似度计算方式应用 应用Spark完成文章相似度计算 2.7.1 文章相似度 在我们的某项目推荐中有很多地方需要推荐相似文章&#xff0c;包括首页频道可以推荐相似的文章&#xff0c;详情…

详解Map和Set

目录 一、二叉搜索树 1、概述 2、模拟实现搜索二叉树 a、向搜索二叉树中插入数据 b、查找二叉搜索树的指定值的结点 c、删除二叉树的指定值的结点 3、对二叉搜索树进行性能分析 二、Map的使用 1、Map简介 2、Map常用方法 ​编辑三、Set的使用 1、Set简介 2、S…

零基础学习笔记 - ADF4159

目录1.准备工作1.1.前言1.2.资料1.3.介绍1.4.应用1.5.应用电路2.ADF41592.1.功能框图2.2.通信协议时序2.2.寄存器2.2.0.注意2.2.1.延迟寄存器(R7)映射2.2.2.步进寄存器(R6)映射2.2.3.偏差寄存器(R5)映射2.2.4.时钟寄存器(R4)映射2.2.5.功能寄存器(R3)映射2.2.6.R分频器寄存器(R…

Batchsize的大小怎样设置?Batchsize过大和过小有什么影响

一、Batchsize基本介绍 1. Batchsize是什么 batch_size:表示单次传递给程序用以训练的数据(样本)个数。如果我们的数据集钟含有的样本总数为12800个样本,batch_size=128,那么就需要10个batch才能够训练完一个epoch。 batch_size一般取值为2的N次幂的形式,这是因为CPU或…

高级性能测试系列《38.Arrivals Thread Group、ConcurrencyThread Group、终极线程组》

一、面向目标&#xff1a;Arrivals Thread Group需求&#xff1a;要做一个秒杀&#xff0c; 能支持1000个人同时秒杀&#xff0c;我们的系统不能崩溃。错误案例示范1秒内的人数的运行是有先后的&#xff0c;1000个人在1秒钟内启动&#xff0c;运行完毕一次就停掉了。由图可以看…

Cadence PCB仿真使用Allegro PCB SI查看仿真波形的方法图文教程

🏡《Cadence 开发合集目录》   🏡《Cadence PCB 仿真宝典目录》 目录 1,概述2,拓扑提取阶段仿真方法3,图纸设计阶段仿真方法4,总结1,概述 本文简单介绍使用Alegro PCB SI执行仿真查看仿真波形的两种方法。 2,拓扑提取阶段仿真方法 如下图在拓扑提取阶段,添加完激励…

走进后端开发流程 | 青训营笔记

目录 一、走进后端开发流程 1、传统流程 2、敏捷开发 3、SAFe简介 4、字节团队的开发流程 二、开发流程详解 1、需求阶段 2、开发阶段 云原生开发 团队的分支策略 自测 3、测试阶段 4、发布阶段 简单发布 金丝雀发布 滚动发布&#xff08;推荐&#xff09; 蓝…

记录每日LeetCode 160.相交链表 Java实现

题目描述&#xff1a; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&…

进程和线程

1.关于进程进程基本概念进程&#xff08;process&#xff09;也叫任务&#xff08;task&#xff09;。进程是操作系统对一个正在运行的程序的一种抽象。也就是说&#xff0c;可以把进程看作程序的一次运行过程。 &#xff08;一个正在运行的程序——>进程。没有跑起来就不算…

CSS语法与CSS选择器

目录 CSS 语法 实例 例子解释 CSS 选择器 CSS 元素选择器 实例 CSS id 选择器 实例 CSS 类选择器 实例 实例 实例 CSS 通用选择器 实例 CSS 分组选择器 实例 所有简单的 CSS 选择器 延伸阅读 CSS 语法 CSS 规则集&#xff08;rule-set&#xff09;由选择器和…

java spring IOC外部Bean注入

外部Bean注入也是一种Bean操作的属性注入 但这次我们要注入的是一个类对象 我们先创建spring项目 引入基本依赖 然后在src下创建两个包 gettingStarted 和 generate 这个名字可以随便取 但和我同名 可以让你们不会出现 名称不一样导致资源找不到的问题 然后在 gettingStarte…

【寒假每日一题】AcWing 4729. 解密(补)

文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴韦达定理及其逆定理一、题目 1、原题链接 4729. 解密 2、题目描述 给定一个正整数 k&#xff0c;有 k次询问&#xff0c;每次给定三个正整数 ni,ei,di&#xff0c;求两个正…

腾讯云GPU服务器环境部署与连接配置

先前博主购买了腾讯云的GPU服务器后&#xff0c;发现上面预装的环境存在一些问题&#xff0c;因此便来重新部署一下。 为了操作方便&#xff0c;博主这里使用了一个远程控制端软件&#xff1a;Xshell 博主在初始化时已经安装过pytorch了&#xff0c;我们首先看看安装的路径 测…