【Hello Algorithm】认识一些简单的递归

简介: 【Hello Algorithm】认识一些简单的递归

我在刚刚学习C语言的时候写过一个汉诺塔问题 大家可以参考下我之前写的这篇博客

汉诺塔问题

其实这个问题也可以这么解决

  • 我们设计六个函数 这六个函数分别代表从a到b 从a到c …
  • 但是实际上我们使用了一个函数就解决了上面六个函数才能解决的事

这就说明 我们可以使用增加参数的方式来增加递归函数的可能性

打印一个字符串全部的子序列

题目要求如下

假设字符串String str = “abc”,那么它的子序列有" "、a、b、c、ab、ac、bc、abc。依次从头往后拿,所有情况都枚举即可

我们回顾下题目实际上就是从第一个字符串开始我们考虑每个字符要和不要两种情况

那么我们可以设计这样子的一个递归函数

void process(string str ,int index , list<string> ans , string path)

这四个参数分别代表着

  • 我们要传入的字符串
  • 现在走到的位置
  • 答案
  • 目前为止拼接的字符串

我们每个递归函数内部只需要考虑当前字符是否拼接 之后传递给下一个递归函数即可

完整代码如下

  void process(const string& str ,int index , list<string>& ans , string path1)
  {
    if (str.length() == index)
    {
      ans.push_back(path1);
      return;    
    }    
    process(str , index + 1 , ans , path1);    
    process(str , index + 1 , ans , path1 + str[index]);    
  }

如果不允许有重复的字符串出现应该怎么做呢?

此时我们就可以使用 unordered_set 容器来自动去重即可

打印一个字符串的全排列

输入一个字符串 打印这个字符串的全排列

比如说给你一个字符串 abc

实际上就是给你一个字符集合 {a, b, c} 要求你将这三个字符随意组合出一个新的字符 共有多少种排列方式

实际上就是 3 x 2 x 1 一共六种

我们解决这个问题使用递归来解决 首先递归函数的声明如下

void  process(vector<char> lc , list<string>& ans , string path1)

代码如下

  void  process(vector<char> lc , list<string>& ans , string path1)    
  {    
    if (lc.size() == 0)    
    {    
      ans.push_back(path1);    
      return;    
    }    
    else    
    {    
      for (int i = 0; i < lc.size(); i++)    
      {    
        char cur = lc[i];    
        lc.erase(lc.begin() + i);    
        process(lc , ans , path1 + cur);                                                                                        
        lc.insert(lc.begin() + i , cur);    
      }    
    }    
  }  

解释下上面这段代码 我们每次都从集合中拿走一个元素 如果说集合被拿空了 那么此时的字符串肯定已经拼接完成了

如果集合还没空我们就一个个将这些字符串拼接起来 (注意! 如果你是将集合拼接在path中则要在最后还原path 如果是将path和cur一起传递则不用)

最后我们就能得到一个字符串的全排列了

742d31b8224e488696c42db51236e069.png

1734989da64341479f8777497b386c08.png

当然 我们上面写的其实是一种不太好的递归 因为它可变参数的设计不太行 这样子我们递归到动态规划就会很麻烦 优秀的递归设计应该是下面这样子的

  void process(vector<char>& nums , int index , vector<string>& ans)    
  {    
    if (nums.size() == index)    
    {    
      string ans1;    
      for (auto x : nums)    
      {    
        ans1 += x;    
      }    
      ans.push_back(ans1);    
      return;    
    }    
    else    
    {    
      for (size_t i = index ; i < nums.size() ; i++)    
      {    
        swap(nums[i] , nums[index]);    
        process(nums , index + 1 , ans);    
        swap(nums[i] , nums[index]);    
      }                                                                                                                         
    }    
  } 

给定我们一个原数组 我们从数组中选择一个字符作一号位

for (size_t i = index ; i < nums.size() ; i++)
swap(nums[i] , nums[index]);

选择完一号位之后我们再选二号位 之后依次递归即可

process(nums , index + 1 , ans);

如果要求字符串不重复应该怎么做呢?

如果要求字符串不重复 这里有两种方式

  1. 我们使用unordered_set容器来保存答案
  2. 进行剪枝

我们可以设置一个bool数组 如果说某个元素已经做个头了 我们就不让它做头了

未剪枝前

07142ac764854a56b0e5b9de8f45cd74.png剪枝代码

b07787a22d814803b00a76fa7b2d3d56.png

剪枝后

5d793b68267c45638172d84565bdd64f.png

不申请额外的空间 逆序输出一个栈

我们现在申请一个栈 现在它内部有 1 2 3 4 5 6

如果我们正常输出 应该是这样子的

185b2eb4e7f44d97ac66624654f689fc.png

现在我们要逆序这个栈的输出 我们可以这么做

void revprint(stack<int>& st)    
{    
  if (st.empty())    
  {    
    return;    
  }    
  else    
  {    
    int top = st.top();    
    st.pop();    
    revprint(st);    
    cout << top << " ";                                                                                                         
  }    
}  

9d98cdf5c9e149ae8a924cf31324b7d1.png使用系统栈 等消栈的时候打印即可

相关文章
|
测试技术 领域建模 定位技术
基于事件风暴的需求分析 | 方法案例一
事件风暴(Event Storming)源自领域驱动设计社区,由 Alberto Brandolini 在2012 年发明[1]。 事件风暴最早的名字是基于事件的建模(Event-Based Modeling),正如这个名字所暗示的,事件风暴在发明之初的核心目的是领域建模,在今天的大多数文献和实践中,事件风暴的核心关注点都是领域模型和软件架构。
5330 2
基于事件风暴的需求分析 | 方法案例一
|
1月前
|
域名解析 人工智能 弹性计算
阿里云备案服务码:使用指南与10大常见问题解答,优惠助力备案无忧
阿里云备案服务码的完整申请与使用指南,明确备案服务码需通过购买符合要求且时长3个月以上的阿里云服务器或云虚拟主机等产品获取。文章分别梳理了云服务器、云虚拟主机两类产品的控制台申请操作路径,清晰说明备案服务码的本账号使用、跨账号授权规则,以及云产品退款/释放对服务码有效性的影响等核心管理规则,详细讲解了在备案系统中填入服务码的操作步骤。文末汇总了用户高频遇到的10类常见问题与对应解决方案,同时附上阿里云2026年云产品与AI产品的相关优惠权益入口,帮助用户低成本获取符合要求的云资源,顺利完成ICP备案全流程。
|
4月前
|
人工智能 数据挖掘 iOS开发
Codex大更新,咱普通人怎么用它交付工作
老金我今天看Codex这波更新,越看越觉得符合我这种产品人了。 它真正想干的事,是把AI从聊天框里拽出来,放进一个能交付结果的工作台。你不一定会写代码,也不一定懂什么仓库、分支、命令行。你真正要关心的是:我手里这堆资料,能不能变成一个别人能打开、能检查、能继续推进的东西。 ​ ![Image](https://ucc.alicdn.com/pic/developer-ecology/p3sh
|
网络协议 网络安全
华为openEuler欧拉系统的防火墙常用设置方法
在有防火墙的系统中,比如我们部署了一个WEB项目,我们需要给该WEB项目所需的端口放行。本文介绍了防火墙常用设置方法。
3227 81
|
9月前
|
监控 Java Shell
告别繁琐命令行:自研多线程 SSH 极速文件传输助手(附 GitHub 源码)
SSH Turbo File Transfer 是一款轻量开源SFTP桌面工具,基于Python/Tkinter开发。支持多线程并行传输、实时流量监控、SSH命令自动解析、双进度条及跨平台运行,专为高效处理大量小文件而设计,大幅提升远程文件传输体验。(239字)
421 4
|
10月前
|
运维 Prometheus 监控
运维不是救火队
运维不是救火队
434 6
|
人工智能 自然语言处理 开发工具
如何提升 AI 漫剧创作的流畅性与创新性?一次工程视角下的实践与测评
本文记录AI漫剧项目实战经验:初期直连模型接口效率高但难维护;引入聚合平台缓解碎片化;最终通过工程抽象统一AI能力接入,提升稳定性与可维护性。强调“流程稳定>单次效果”,适配不同阶段的技术选型策略。(239字)
|
11月前
|
XML 自然语言处理 JavaScript
【车载Android】使用自定义插件实现多语言自动化适配
2024年中国跃居全球第一大汽车出口国,车载Android应用全球化需求激增。为解决多语言适配繁琐、易错问题,开发者推出开源插件`MultilingualPlugin`,支持Excel驱动翻译、自动生成功能,覆盖多模块工程与增量更新,大幅提升开发效率。
424 2
【车载Android】使用自定义插件实现多语言自动化适配
|
12月前
|
人工智能 开发者
从技术到品牌:一个AI指令,让开发者也能写出动人的品牌故事
开发者常擅技术却困于品牌叙事。本文分享一套结构化AI指令,结合DeepSeek、通义千问等国产工具,将品牌故事拆解为可执行模块,助力技术人快速生成有温度、有逻辑的品牌故事框架,实现从代码到共鸣的跨越。
973 5
|
11月前
|
JavaScript API 调度
Ref 和 Reactive 响应式原理剖析与代码实现
本文深入剖析 Vue 3 响应式核心原理,详解 `ref` 与 `reactive` 的区别与实现机制。通过手写简化版响应式系统,揭示基于 Proxy 的依赖收集、触发更新、自动解包等关键逻辑,助你掌握 Composition API 的底层思想。(238 字)
872 0