算法详解-递归
创始人
2025-05-31 07:57:39
0

文章目录

  • 前言
  • 主要内容
    • 初级
      • 阶乘
      • 斐波那契数列
      • 汉诺塔
      • 数组求和
      • 幂运算
      • 数组翻转
      • 字符串翻转
    • 中级
      • 全排列
      • 子集
    • 高级
      • 正则表达式匹配
      • N皇后问题
  • 总结
    • 无限循环
    • 栈溢出
  • 更多宝藏


前言

😎🥳😎🤠😮🤖🙈💭🍳🍱
递归是一种非常重要的编程技巧,它可以让我们用简洁的代码解决复杂的问题。


主要内容

🦞🦐🦀🦑🦪

主要内容: 递归是一种算法,它通过调用自身来解决问题。一个递归函数通常包括两个部分:基本情况(base case)和递归情况(recursive case)。基本情况是指函数能够直接解决的最简单问题,而递归情况则是指函数通过调用自身来解决更小规模的问题。

初级

阶乘

举个例子,我们可以使用递归来计算阶乘。阶乘表示为n!,定义为n * (n-1) * (n-2) * … * 1。我们可以将其写成一个递归函数:

int factorial(int n) {if (n == 0) {return 1; // 基本情况} else {return n * factorial(n - 1); // 递归情况}
}

斐波那契数列

斐波那契数列:斐波那契数列的第n项定义为前两项之和(除了第0项和第1项分别为0和1)。我们可以使用递归来计算斐波那契数列的第n项:

#include 
using namespace std;int fib(int n) {if (n == 0 || n == 1) {return n;} else {return fib(n - 1) + fib(n - 2);}
}int main() {int n = 10;cout << fib(n) << endl;return 0;
}

在这个例子中,fib函数接受一个参数:整数n。函数使用递归来计算斐波那契数列的第n项。
当n等于0或者1时,直接返回n作为结果。否则,返回前两项之和。

汉诺塔

汉诺塔问题:汉诺塔是一个经典的递归问题。它包括三根柱子和一些大小不等的圆盘。初始时,所有圆盘都叠在第一根柱子上,按照从大到小的顺序排列。目标是将所有圆盘移动到第三根柱子上,并保持原有顺序。每次移动只能移动一个圆盘,并且不能将大圆盘放在小圆盘上面。

我们可以使用递归来解决汉诺塔问题。假设我们要将n个圆盘从柱子A移动到柱子C,可以分为以下步骤:

将前n-1个圆盘从柱子A移动到柱子B(借助柱子C)。
将最后一个圆盘从柱子A移动到柱子C。
将前n-1个圆盘从柱子B移动到柱子C(借助柱子A)。
其中步骤1和步骤3都是规模更小的汉诺塔问题,因此可以使用递归来解决。

#include 
using namespace std;void hanoi(int n, char A, char B, char C) {if (n == 1) {cout << "Move disk 1 from " << A << " to " << C << endl;} else {hanoi(n - 1, A, C, B);cout << "Move disk " << n << " from " << A << " to " << C << endl;hanoi(n - 1, B, A, C);}
}int main() {int n = 3; // 圆盘数量hanoi(n, 'A', 'B', 'C');return 0;
}

在这个例子中,hanoi函数接受4个参数:圆盘数量n,起始柱子A,辅助柱子B和目标柱子C。函数使用递归来解决汉诺塔问题,并输出每一步的移动过程。

当圆盘数量为1时,直接将圆盘从起始柱子移动到目标柱子。否则,先将前n-1个圆盘从起始柱子移动到辅助柱子(借助目标柱子),然后将最后一个圆盘从起始柱子移动到目标柱子,最后再将前n-1个圆盘从辅助柱子移动到目标柱子(借助起始柱子)。

数组求和

给定一个整数数组,使用递归计算数组中所有元素的和。

#include 
using namespace std;int sum(int arr[], int n) {if (n == 0) {return 0;} else {return arr[n - 1] + sum(arr, n - 1);}
}int main() {int arr[] = {1, 2, 3, 4};int n = sizeof(arr) / sizeof(arr[0]);cout << sum(arr, n) << endl;return 0;
}

在这个例子中,sum函数接受两个参数:整数数组arr和数组长度n。函数使用递归来计算数组中所有元素的和。

当数组长度为0时,返回0作为结果。否则,返回最后一个元素加上前n-1个元素的和。

幂运算

给定两个整数x和n,使用递归计算x的n次方。

#include 
using namespace std;int power(int x, int n) {if (n == 0) {return 1;} else if (n % 2 == 0) {int y = power(x, n / 2);return y * y;} else {return x * power(x, n - 1);}
}int main() {int x = 2;int n = 10;cout << power(x, n) << endl;return 0;
}

在这个例子中,power函数接受两个参数:底数x和指数n。函数使用递归来计算x的n次方。

当指数为0时,返回1作为结果。否则,根据指数的奇偶性分别处理。如果指数是偶数,则将其除以2并递归计算x的(n/2)次方,然后将结果平方;如果指数是奇数,则将其减去1并递归计算x的(n-1)次方,然后将结果乘以x。

数组翻转

给定一个整数数组,使用递归将数组翻转

#include 
using namespace std;void reverse(int arr[], int start, int end) {if (start >= end) {return;} else {swap(arr[start], arr[end]);reverse(arr, start + 1, end - 1);}
}int main() {int arr[] = {1, 2, 3, 4};int n = sizeof(arr) / sizeof(arr[0]);reverse(arr, 0, n - 1);for (int i = 0; i < n; i++) {cout << arr[i] << " ";}cout << endl;return 0;
}

在这个例子中,reverse函数接受三个参数:整数数组arr、起始位置start和结束位置end。函数使用递归来将数组翻转。
当起始位置大于等于结束位置时,直接返回。否则,交换两端的元素,并对剩余部分进行递归翻转。

字符串翻转

给定一个字符串,使用递归将字符串翻转。

#include 
#include 
using namespace std;string reverse(string str) {if (str.length() <= 1) {return str;} else {char first = str[0];string rest = str.substr(1);return reverse(rest) + first;}
}int main() {string str = "hello";cout << reverse(str) << endl;return 0;
}

在这个例子中,reverse函数接受一个参数:字符串str。函数使用递归来将字符串翻转。

当字符串长度小于等于1时,直接返回字符串本身作为结果。否则,取出第一个字符和剩余部分,并对剩余部分进行递归翻转,然后将第一个字符添加到末尾。

中级

全排列

给定一个字符串,使用递归打印出该字符串的所有排列。

#include 
#include 
using namespace std;void permute(string str, int l, int r) {if (l == r) {cout << str << endl;} else {for (int i = l; i <= r; i++) {swap(str[l], str[i]);permute(str, l + 1, r);swap(str[l], str[i]);}}
}int main() {string str = "ABC";int n = str.length();permute(str, 0, n - 1);return 0;
}

在这个例子中,permute函数接受三个参数:字符串str、起始位置l和结束位置r。函数使用递归来打印出字符串的所有排列。

当起始位置等于结束位置时,直接打印字符串作为结果。否则,枚举起始位置到结束位置之间的每一个字符,并将其与起始位置的字符交换,然后对剩余部分进行递归排列。

子集

给定一个整数数组,使用递归打印出该数组的所有子集。

#include 
#include 
using namespace std;void subsets(vector &arr, vector> &result, vector &subset, int index) {result.push_back(subset);for (int i = index; i < arr.size(); i++) {subset.push_back(arr[i]);subsets(arr, result, subset, i + 1);subset.pop_back();}
}int main() {vector arr = {1, 2 ,3};vector> result;vector subset;subsets(arr,result ,subset ,0);for(int i=0;icout<<"[";for(int j=0;jcout<

这段代码是一个使用递归生成整数数组所有子集的C++程序。它定义了一个名为subsets的函数,该函数接受四个参数:整数数组arr、结果集合result、当前子集合并subset和当前索引位置index。

在主函数中,首先定义了一个整数数组arr = {1, 2 ,3},然后定义了一个结果集合和一个空的子集合并。接着调用了一次subsets(arr,result ,subset ,0)函数,以生成所有子集。

最后,程序遍历结果集合并打印出每个子集。

高级

正则表达式匹配

给定一个字符串s和一个正则表达式p,使用递归实现正则表达式匹配。

#include 
#include 
using namespace std;bool isMatch(string s, string p) {if (p.empty()) {return s.empty();}bool first_match = (!s.empty() && (s[0] == p[0] || p[0] == '.'));if (p.length() >= 2 && p[1] == '*') {return (isMatch(s, p.substr(2)) || (first_match && isMatch(s.substr(1), p)));} else {return first_match && isMatch(s.substr(1), p.substr(1));}
}int main() {string s = "aa";string p = "a*";cout << isMatch(s, p) << endl;return 0;
}

在这个例子中,isMatch函数接受两个参数:字符串s和正则表达式p。函数使用递归来实现正则表达式匹配。

当正则表达式为空时,如果字符串也为空,则返回true;否则返回false。接着判断第一个字符是否匹配。如果正则表达式的长度大于等于2且第二个字符为’*',那么可以选择忽略这两个字符或者重复第一个字符。否则,继续比较剩余部分。

N皇后问题

给定一个整数n,表示棋盘的大小为n*n,使用递归求出所有不同的n皇后摆放方案。

#include 
#include 
#include 
using namespace std;bool isValid(vector &board, int row, int col) {int n = board.size();for (int i = 0; i < row; i++) {if (board[i][col] == 'Q') {return false;}}for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (board[i][j] == 'Q') {return false;}}for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if (board[i][j] == 'Q') {return false;}}return true;
}void backtrack(vector> &res, vector &board, int row) {if(row==board.size()){res.push_back(board);return ;}int n=board[row].size();for(int col=0;colif(!isValid(board,row,col)){continue;}board[row][col]='Q';backtrack(res,board,row+1);board[row][col]='.';}}vector> solveNQueens(int n) {vector> res;vector board(n,string(n,'.'));backtrack(res,board,0);return res;}int main() {int n=4;vector> result=solveNQueens(n);for(int k=0;kcout<<"Solution "<cout<

这段代码是一个使用递归解决N皇后问题的C++程序。它定义了三个函数:isValid、backtrack和solveNQueens。

其中,isValid函数用于判断在棋盘的第row行第col列放置一个皇后是否合法。它接受三个参数:棋盘board、行号row和列号col。函数通过遍历棋盘上方、右上方和左上方来判断是否有其他皇后与当前位置冲突。

backtrack函数用于回溯搜索所有可能的解。它接受三个参数:结果集合res、棋盘board和当前行号row。当row等于棋盘大小时,表示找到了一种解法,将其加入结果集合中。否则,遍历当前行的每一列,如果在该位置放置皇后合法,则继续搜索下一行。

solveNQueens函数是主要的求解函数。它接受一个整数n作为参数,表示棋盘的大小为n*n。函数首先定义了一个结果集合res和一个空棋盘board,然后调用一次backtrack(res, board, 0)函数以搜索所有可能的解。

在主函数中,首先定义了一个整数n=4,表示棋盘大小为4*4。然后调用solveNQueens(n)函数求解,并打印出所有不同的摆放方案。


总结

🐋 🐬 🐶 🐳 🐰 🦀☝️ ⭐ 👉 👀
递归是一种强大而优雅的编程技巧,在解决复杂问题时非常有用。但是使用时需要注意避免无限循环和栈溢出等问题。

无限循环

避免无限循环是编写递归函数时需要注意的一个重要问题。如果递归函数没有正确地定义基本情况和递归情况,就可能出现无限循环的情况。

为了避免无限循环,我们需要确保每次递归调用都能够使问题的规模减小,最终能够达到基本情况。这样,递归函数就能够在有限的步骤内结束。

例如,在计算阶乘的例子中,我们定义了两种情况:当n等于0时,直接返回1作为结果;否则,返回n乘以(n-1)!。由于每次递归调用都会将n减小1,因此最终一定会达到基本情况n等于0。

另外,在编写递归函数时,也可以使用一些技巧来避免无限循环。例如,可以使用计数器来记录递归调用的次数,并在超过一定次数时强制结束递归。

总之,要避免无限循环,关键是要正确地定义基本情况和递归情况,并确保每次递归调用都能够使问题的规模减小。

栈溢出

栈溢出(stack overflow)是指程序在运行过程中,栈空间被耗尽的情况。这通常是由于程序中存在无限递归或者过深的递归调用导致的。

当程序运行时,每次函数调用都会在栈上分配一定的空间来存储局部变量、参数和返回地址等信息。当函数返回时,这些空间会被释放。如果函数调用层次过深,栈上分配的空间就会超过栈的容量,导致栈溢出。

栈溢出可能会导致程序崩溃或者产生不可预料的行为。因此,在编写递归函数时,应该注意避免无限递归和过深的递归调用。

如果你在解决这些问题时遇到困难,不妨多思考一下,或者寻求其他人的帮助。只要坚持不懈,相信你一定能够掌握递归这一重要技能。


更多宝藏

🍇🍉🍊🍏🍋🍅🥝🥥🫒🫕🥗
项目仓库看这里🤗:
https://github.com/w-x-x-w
https://gitee.com/w-_-x
博客文章看这里🤭:
https://blog.csdn.net/weixin_62650212
视频推送看这里🤤:
https://space.bilibili.com/1909782963

相关内容

热门资讯

武汉摩尔影城安卓系统APP,便... 你有没有想过,一部手机就能带你走进电影的世界,享受大屏幕带来的震撼?今天,就让我带你详细了解武汉摩尔...
联想刷安卓p系统,畅享智能新体... 你有没有发现,最近联想的安卓P系统刷机热潮可是席卷了整个互联网圈呢!这不,我就迫不及待地来和你聊聊这...
mac从安卓系统改成双系统,双... 你有没有想过,你的Mac电脑从安卓系统改成双系统后,生活会有哪些翻天覆地的变化呢?想象一边是流畅的苹...
kindke安卓系统激活码,激... 亲爱的读者,你是否在寻找一款能够让你手机焕然一新的操作系统?如果你是安卓用户,那么今天我要给你带来一...
萤石云监控安卓系统,安卓系统下... 你有没有想过,家里的安全可以随时随地掌握在手中?现在,有了萤石云监控安卓系统,这不再是梦想啦!想象无...
手机安卓系统会不会爆炸,系统升... 手机安卓系统会不会爆炸——一场关于安全的探讨在当今这个数字化的世界里,手机已经成为我们生活中不可或缺...
安卓系统双清详图解,恢复出厂设... 你有没有遇到过手机卡顿、运行缓慢的问题?别急,今天就来给你详细解析一下安卓系统的“双清”操作,让你的...
召唤抽奖系统安卓直装,轻松体验... 你知道吗?现在市面上有一种特别火的玩意儿,那就是召唤抽奖系统安卓直装。是不是听起来就让人心动不已?没...
系统工具箱安卓2.3,深度解析... 你有没有发现,手机里的那些小工具,有时候就像是个神奇的百宝箱呢?今天,就让我带你一探究竟,看看安卓2...
华硕平板安卓刷机系统,解锁性能... 亲爱的数码爱好者们,你是否曾为你的华硕平板安卓系统感到厌倦,想要给它来一次焕然一新的体验呢?那就跟着...
鸿蒙系统与安卓怎么区别,差异解... 你有没有发现,最近手机圈子里有个大热门,那就是鸿蒙系统和安卓系统的区别。这两位“系统大侠”各有各的绝...
红帽系统怎么刷回安卓,红帽系统... 你是不是也和我一样,对红帽系统刷回安卓充满了好奇?别急,今天就来给你详细揭秘这个过程,让你轻松上手,...
ios安卓联想三系统,全面解析... 你有没有发现,现在的手机市场真是热闹非凡呢!各种操作系统轮番登场,让人眼花缭乱。今天,就让我带你来聊...
安卓调用系统相机并存盘,And... 你有没有想过,手机里的照片和视频,是怎么被我们随手拍下,又神奇地存到手机里的呢?今天,就让我带你一探...
安卓4.0原生系统下,引领智能... 你有没有发现,安卓4.0原生系统下,手机的使用体验简直就像打开了新世界的大门?今天,就让我带你一起探...
安卓c13系统,创新功能与性能... 你知道吗?最近安卓系统又来了一次大更新,那就是安卓C13系统。这可不是一个小打小闹的更新,而是带来了...
鸿蒙3.0脱离安卓系统,开启全... 你知道吗?最近科技圈可是炸开了锅,因为华为的新操作系统鸿蒙3.0横空出世,竟然宣布要脱离安卓系统,这...
安卓怎么应对苹果系统,安卓系统... 你知道吗?在智能手机的世界里,安卓和苹果就像是一对相爱相杀的恋人。安卓系统,这位多才多艺的“大众情人...
安卓系统如何开橱窗教程,安卓系... 你有没有想过,你的安卓手机里也能开个橱窗,展示那些你心爱的宝贝?没错,就是那种可以随时翻看、随时分享...
安卓系统软件APK,深入探究安... 你有没有发现,手机里的那些好玩的应用,其实都是靠一个小小的文件来“住”进去的?没错,就是安卓系统里的...