算法入门<二>:分治算法之汉诺塔问题及递归造成的栈溢出

1、分治算法

  分治(divide and conquer),全称分而治之,是一种非常重要且常见的算法策略。分治通常基于递归实现,包括“分”和“治”两个步骤。

  • (划分阶段):递归地将原问题分解为两个或多个子问题,直至到达最小子问题时终止。
  • (合并阶段):从已知解的最小子问题开始,从底至顶地将子问题的解进行合并,从而构建出原问题的解。

一个问题是否适合使用分治解决,通常可以参考以下几个判断依据。

  • 问题可以分解:原问题可以分解成规模更小、类似的子问题,以及能够以相同方式递归地进行划分。
  • 子问题是独立的:子问题之间没有重叠,互不依赖,可以独立解决。
  • 子问题的解可以合并:原问题的解通过合并子问题的解得来。

  分治不仅可以有效地解决算法问题,往往还可以提升算法效率。在排序算法中,快速排序、归并排序、堆排序相较于选择、冒泡、插入排序更快,就是因为它们应用了分治策略。

2、汉诺塔问题

2.1 传说

  大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。 简化后类似如下:
在这里插入图片描述

2.2 分治策略

  解决汉诺塔问题的分治策略:将原问题f(n)划分为两个子问题f(n-1)和一个子问题f(1),并按照以下顺序解决这三个子问题。

  1. 将n-1个圆盘借助 C 从 A 移至 B 。
  2. 将剩余1个圆盘从 A 直接移至 C 。
  3. 将n-1个圆盘借助 A 从 B 移至 C 。

  对于这两个子问题 f(n-1),可以通过相同的方式进行递归划分,直至达到最小子问题 f(1)。而f(1)的解是已知的,只需一次移动操作即可。
在这里插入图片描述

2.3 代码实现

/* 求解汉诺塔问题 f(i) *//* 移动一个圆盘 */
void move(vector<int>& src, vector<int>& tar) 
{// 从 srcA 顶部拿出一个圆盘int pan = src.back();src.pop_back();// 将圆盘放入 tarC 顶部tar.push_back(pan);
}void dfs(int nSize, vector<int>& srcA, vector<int>& bufB, vector<int>& tarC,int& nMoveTimes)
{// 若 srcA 只剩下一个圆盘,则直接将其移到 tarCif (nSize == 1){nMoveTimes++;move(srcA, tarC);return;}// 子问题 f(i-1) :将 srcA 顶部 i-1 个圆盘借助 tarC 移到 bufBdfs(nSize - 1, srcA, tarC, bufB, nMoveTimes);// 子问题 f(1) :将 srcA 剩余一个圆盘移到 tarCmove(srcA, tarC);// 子问题 f(i-1) :将 bufB 顶部 i-1 个圆盘借助 srcA 移到 tarCdfs(nSize - 1, bufB, srcA, tarC, nMoveTimes);
}void solveHanota(vector<int>& A, vector<int>& B, vector<int>& C, int& nMoveTimes)
{int nSize = A.size();// 将 A 顶部 n 个圆盘借助 B 移到 Cdfs(nSize, A, B, C, nMoveTimes);
}int main()
{int nObject = 25;vector<int> vecObject;for (int i = nObject; i > 0; i--){vecObject.push_back(i);}int nMoveTimes = 0;vector<int> objectB;vector<int> objectC;solveHanota(vecObject, objectB, objectC, nMoveTimes);std::cout << "放置圆盘" << nObject << "个,共计需要移动" << nMoveTimes << "次!" << endl;return 0;
}
放置圆盘25个,共计需要移动16777216次!

2.4 代码漏洞

  当盘子数量为64的话,一共需要移动约1800亿亿步(18,446,744,073,709,551,615),才能最终完成整个过程。即使借助于计算机,假设计算机每秒能够移动100万步,那么约需要18万亿秒,即58万年。将计算机的速度再提高1000倍,即每秒10亿步,也需要584年才能够完成。所以上述解法无法处理大数据量问题

3、递归导致栈溢出

  递归算法在数学问题上会经常出现,但运用不当会出问题。有的是运算时间太长如上面的汉诺塔问题,有的则是栈溢出,例如下面代码。我们着重说一下栈溢出问题。

// 递归求和函数当n=4750会出现栈溢出问题
void Sum(int& nSum, int n)
{if (n == 0){return;}nSum += n;n--;Sum(nSum, n);
}
// 斐波那契数列 0,1,1,2,3,5,8…… 递归树 时间复杂度高 会卡死
unsigned int fib(int n)
{if (n == 1 || n == 2){return n - 1;}return fib(n - 2) + fib(n - 1);
}
// 阶乘 时间复杂度高 卡死
unsigned int factorialRecur(unsigned int n)
{if (n == 0){return 1;}int count = 0;for (int i = 0; i < n; i++){count += factorialRecur(n - 1);}return count;
}

3.1 为什么会栈溢出?

  计算机的内存可以分为三个区域:栈区,堆区,静态区。它们在存储使用时遵循不同的规则,并且存储的内容也不同。其中栈内存速度最快但空间很小一般只有几兆M大小,而我们每一次递归时,上一次的递归程序仍然没有结束,也就是上一次递归的函数仍然占据着内存栈区的空间; 递归调用函数次数太多栈就会溢出。

在这里插入图片描述

3.2 解决方式

  对于栈溢出的问题一般通过减少递归的层数来解决。比如下面的求和问题。

// 递归求和函数当n=4750会出现栈溢出问题
void Sum(int& nSum, int n)
{if (n == 0){return;}nSum += n;n--;Sum(nSum, n);
}// 迭代求和  可正常计算
unsigned int Sum(int n)
{int nSum = 0;for (int a = 1; a <= n; a++){nSum += a;}return nSum;
}

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

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

相关文章

ARM学习(27)链接库依赖学习(二)dlopen failed:library xxxx.so

笔者继续学习一下链接的依赖库。 1、起因 Android下面需要需要一个日志解码库&#xff0c;所以笔者就编译了一个parse.so来进行解码&#xff0c; 编译器&#xff1a;Clang&#xff0c;基于llvm后端的编译器平台&#xff1a;交叉编译&#xff0c;linux -> aarch64 linux An…

Angular中的管道(Pipe)

Angular中的管道(Pipe) 文章目录 Angular中的管道(Pipe)前言一、内置管道1. date管道格式化日期2. currency管道格式化货币3. uppercase和lowercase管道转换字符串大小写4. 小数位数5. JavaScript 对象序列化6. slice7. 管道链 二、自定义管道 前言 Angular中的管道&#xff0…

【Qt之OpenGL】01创建OpenGL窗口

1.创建子类继承QOpenGLWidget 2.重写三个虚函数 /** 设置OpenGL的资源和状态,最先调用且调用一次* brief initializeGL*/ virtual void initializeGL() override; /** 设置OpenGL视口、投影等&#xff0c;当widget调整大小(或首次显示)时调用* brief resizeGL* param w* para…

四、线段、矩形、圆、椭圆、自定义多边形、边缘轮廓和文本绘制(OpenCvSharp)

功能实现&#xff1a; 对指定图片上进行绘制线段、矩形、圆、椭圆、自定义多边形、边缘轮廓以及自定义文本 一、布局 用到了一个pictureBox和八个button 二、引入命名空间 using System; using System.Collections.Generic; using System.Drawing; using System.Windows.F…

【MySQL | 第九篇】重新认识MySQL锁

文章目录 9.重新认识MySQL锁9.1MySQL锁概述9.2锁分类9.2.1锁的粒度9.2.2锁的区间9.2.3锁的性能9.2.4锁的级别 9.3拓展&#xff1a;意向锁9.3.1意向锁概述9.3.2意向锁分类9.3.3意向锁作用&#xff08;1&#xff09;意向锁的兼容互斥性&#xff08;2&#xff09;例子1&#xff08…

【Flutter】极光推送配置流程(小米厂商通道) 章二

前言 继【Flutter】极光推送配置流程(极光通道/华为厂商/IOS) 章一 并且&#xff0c;我大概率不会去修改第一篇文章的内容。 随着我自己在配置公司的项目的同时&#xff0c;我希望一直更新这个推送系列文章。 在章一配置完后&#xff0c;也是出现了一些问题&#xff0c;所以本…

PHP算命源码_最新测算塔罗源码_可以运营

众筹商城源码 众筹商品平台 商城加共识元富之路 网上商城众筹 前端是编译后的&#xff0c;后端PHP&#xff0c;带商城 运行截图 源码贡献 https://githubs.xyz/boot?app39 部分数据库表 CREATE TABLE ti_shopro_store (id int(11) NOT NULL AUTO_INCREMENT COMMENT ID,nam…

当Kubeflow遇上GPU池化

随着人工智能技术的迅猛发展&#xff0c;AI开发已成为企业创新的重要驱动力。然而&#xff0c;在AI开发过程中&#xff0c;企业面临着诸多挑战&#xff0c;如开发工具的选择和开发资源如何高效利用等。本文将围绕这些挑战&#xff0c;探讨GPU池化如何赋能Kubeflow进行AI开发&am…

实验八智能手机互联网程序设计(微信程序方向)实验报告

请在上一次实验的基础之上完成“手机快速注册”页面、“企业用户注册”页面&#xff0c;并实现点击手机快速注册和企业用户注册后转跳至该页面在“手机快速注册”页面&#xff0c;输入框内输入内容并失去焦点后&#xff0c;下方的按钮会变化 在企业用户注册页面&#xff0c;用户…

【网站项目】木里风景文化管理平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

在Linux操作系统中的磁盘分区管理案例

1.在硬盘sdb上创建不同的分区实例练习 Linux操作系统是安装在硬盘sda硬盘中&#xff0c;所以不要轻易动硬盘sda中的文件信息 有如下需求 创建主分区 500M 文件系统 ext4 挂载点 /web 创建主分区 500M 文件系统 ext4 挂载点 /nginx 创建逻辑分区 500M 文件系…

【webrtc】MessageHandler 4: 基于线程的消息处理:以Fake 收发包模拟为例

G:\CDN\rtcCli\m98\src\media\base\fake_network_interface.h// Fake NetworkInterface that sends/receives RTP/RTCP packets.虚假的网络接口,用于模拟发送包、接收包单纯仅是处理一个ST_RTP包 消息的id就是ST_RTP 类型,– 然后给到目的地:mediachannel处理: 最后消息消…

GPT3 终极指南(二)

原文&#xff1a;zh.annas-archive.org/md5/6de8906c86a2711a5a84c839bec7e073 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第五章&#xff1a;GPT-3 作为企业创新的下一步 当一个新的创新或技术转变发生时&#xff0c;大公司通常是最后一个采纳的。它们的等级结构…

虚拟机安装与配置win7

一、安装镜像 Windows7 64位 ed2k://|file|cn_windows_7_ultimate_with_sp1_x64_dvd_u_677408.iso|3420557312|B58548681854236C7939003B583A8078|/ 建议迅雷下载 二、VMware 安装win7 1.新创自定义虚拟机 2.默认即可 3.iso文件我们自己下载&#xff0c;选择一个空的磁盘 4.…

【记录】Python3| 将 PDF 转换成 HTML/XML(✅⭐⭐⭐⭐pdf2htmlEX)

本文将会被汇总至 【记录】Python3&#xff5c;2024年 PDF 转 XML 或 HTML 的第三方库的使用方式、测评过程以及对比结果&#xff08;汇总&#xff09;&#xff0c;更多其他工具请访问该文章查看。 文章目录 pdf2htmlEX 使用体验与评估1 安装指南2 测试代码3 测试结果3.1 转 HT…

闪存存储和制造技术概述

闪存存储技术 引言 性能由高到低排序&#xff1a;SLC -> MLC -> TLC -> QLC 根据这个排序读写速度也越来越低&#xff0c;价格越来越便宜 1. SLC SLC&#xff08;Single-Level Cell&#xff0c;单层单元&#xff09;&#xff1a; SLC 闪存具有最高的性能、耐用性和可…

【专篇】DDR3 SDRAM-01总体介绍

概念 DDR3 SDRAM(Double-Data-Rate 3 Synchronous Dynamic Random-Access Memory,第三代双倍速率同步动态随机存取存储器)是计算机存储技术的一种重要进步,它在前代DDR2 SDRAM的基础上进行了多项改进和优化。以下是DDR3 SDRAM的特点介绍: 双倍速率(DDR):DDR3 SDRAM采用…

c#数据库: 4.修改学生成绩

将4年级的学生成绩全部修改为100分,。修改前的学生信息表如图所示: using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks;namespace StudentUpdate {internal class Program{s…

Ubuntu如何更换 PyTorch 版本

环境&#xff1a; Ubuntu22.04 WLS2 问题描述&#xff1a; Ubuntu如何更换 PyTorch 版本考虑安装一个为 CUDA 11.5 编译的 PyTorch 版本。如何安装旧版本 解决方案&#xff1a; 决定不升级CUDA版本&#xff0c;而是使用一个与CUDA 11.5兼容的PyTorch版本&#xff0c;您可…

c#数据库: 10.调用存储过程查询信息,并显示在窗体上

查询女生信息&#xff0c;并将信息显示在窗体上: 原数据表//右键数据库名,新建查询 ------------- 新建查询窗口,添加新建存储过程Procedure_GetGirls1和查询代码如下 : CREATE PROCEDURE dbo.Procedure_GetGirls1 /*存储过程名称*/ AS SELECT * f…