当前位置: 首页 > news >正文

【递归、搜索与回溯】专题一:递归(一)

📝前言说明:

  • 本专栏主要记录本人递归,搜索与回溯算法的学习以及LeetCode刷题记录,按专题划分
  • 每题主要记录:(1)本人解法 + 本人屎山代码;(2)优质解法 + 优质代码;(3)精益求精,更好的解法和独特的思想(如果有的话)
  • 文章中的理解仅为个人理解。如有错误,感谢纠错

🎬个人简介:努力学习ing
📋本专栏:C++刷题专栏
📋其他专栏:C语言入门基础,python入门基础,C++学习笔记,Linux
🎀CSDN主页 愚润泽

你可以点击下方链接,进行该专题内不同子专题的学习

点击链接开始学习
导论递归 (一)、递归 (二)
二叉树的深搜穷举 vs 暴搜 vs 深搜
回溯 vs 剪枝综合练习
FloodFill算法记忆化搜索

题目

  • 面试题 08.06. 汉诺塔问题
    • 个人解
  • 21. 合并两个有序链表
    • 优质解
  • 总结
    • 循环(迭代) vs 递归
    • 递归 vs 深搜


面试题 08.06. 汉诺塔问题

题目链接:https://leetcode.cn/problems/hanota-lcci/description/
在这里插入图片描述

个人解

思路:

  • 分析问题
    • n == 1 时,直接把盘子从 A 移到 C;(递归出口)
    • n > 1 时
      • 先把上面 n - 1 个盘子从 A 移到 B(子问题);
      • 再将最大的盘子从 A 移到 C;
      • 再将 B 上 n - 1 个盘子从 B 移到 C(子问题)。
  • 子问题确定函数头:把 n - 1个盘子从 A,借助 C,移动到 B
    • move(int n, vector<int>& source, vector<int>& auxiliary, vector<int>& target)
  • 单个子问题解决方法:即上面 n > 1的处理流程
  • 递归出口:即:n == 1的处理流程

用时:10:00
屎山代码:

class Solution {
public:void move(int n, vector<int>& source, vector<int>& auxiliary, vector<int>& target){if(n == 1) // 当只有一个盘子,直接移动{target.emplace_back(source.back());source.pop_back();return;}// 把前 n - 1 个盘子移动到辅助柱子move(n - 1, source, target, auxiliary); // 移动第 n 个盘子target.push_back(source.back());source.pop_back();// 在把 前 n - 1 个盘子从辅助盘子移动回来move(n - 1, auxiliary, source, target);}void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {move(A.size(), A, B, C);}
};

时间复杂度:O( 2 n 2^n 2n)
空间复杂度:O(n)


21. 合并两个有序链表

题目链接:https://leetcode.cn/problems/merge-two-sorted-lists/description/
在这里插入图片描述

优质解

思路:

  • 分析问题:
    • 每次进行两个链表的头结点比较,然后提取出较小的头结点
    • 再用提出的头结点,链接两个链表(其中一个的头结点被提出)合并好后的结果
  • 子问题:合并有序链表
    • 函数头:Node* dfs(l1, l2); // 给两个链表,返回提出的头结点
  • 单个子问题解决方法:
    • 比较大小,记录小的头结点Node
    • 链接,假如l1->val < l2 -> val,则l1->next = dfs(l1 -> next, l2);
    • 返回合并后链表的头结点,return Node;
  • 递归出口:链表为空
    代码:
class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if(list1 == nullptr || list2 == nullptr)return list1 == nullptr? list2 : list1; // 返回不为空那个,把没连接的部分链上if(list1 -> val < list2 -> val){list1 -> next = mergeTwoLists(list1 -> next, list2);return list1;}else{list2 -> next = mergeTwoLists(list1, list2 -> next);return list2;}}
};

时间复杂度:O(n1 + n2)
空间复杂度:O(min(n1, n2)),递归最深的那一次调用链的长度


总结

循环(迭代) vs 递归

循环和递归都是在处理重复子问题,那什么时候用循环,什么时候用递归?
适用场景:

  • 循环,当展开时,调用都是同一方向的调用(此时可以写成循环),如:迭代处理集合元素(i 的行为只有 – )
  • 递归,出现多种不同的调用自身(会选择不同方向的调用自身),如:树形结构的遍历,分治策略…

循环和递归展开图的特点:
循环遍历数组:
在这里插入图片描述
递归遍历二叉树:
在这里插入图片描述

循环和递归之间的代码可以相互转换。但是,对于递归转换成循环来说:因为函数体内部递归返回结果的时候,函数还没有完全执行完,所以要用一个栈来存储信息。就会有极大的消耗。

简单的转换代码:

在这里插入图片描述

递归 vs 深搜

递归的展开图,其实就是对一棵树做一次深度优先遍历(dfs)


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关文章:

  • Linux sysvinit 系统启动
  • Android HttpAPI通信问题(待解决)
  • 环境扫描电镜对含水样品的观察技术与方法
  • Baklib数据效率引擎赋能企业AI转型
  • 【Vue】Composables 和 Utils 区别
  • wordpress自学笔记 第四节 商城菜单的添加和修改美化
  • 力扣451:根据字符频率排序(桶排序)
  • FPGA前瞻篇-计数器设计与实现实例
  • 代码随想录训练营第二十二天| 101.对称二叉树 100.相同的树
  • Linux 内核参数
  • 判断数组对象中是否某个字段的值有重复
  • 生产环境怎么移除console
  • 数字IC后端培训教程之数字后端项目典型案例分析
  • js 字符串中的特殊字符全部替换成定义对象里面key对应的value值(进阶篇)
  • Python | 赤道频散关系图
  • 【Redis】SDS结构
  • 图形化编程平台的破局之道:从工具同质化到生态差异化
  • 从MCU到SoC的开发思维转变
  • 2024年北理工Python123第六章测验题整理
  • React 播客专栏 Vol.9|React + TypeScript 项目该怎么起步?从 CRA 到配置全流程
  • 受贿3501万余元,中石油原董事长王宜林一审被判13年
  • 技术派|更强的带刀侍卫:从054B型战舰谈谈世界护卫舰发展
  • 北京“准80后”干部兰天跨省份调任新疆生态环境厅副厅长
  • 2025年上海好护士揭晓,上海护士五年增近两成达12.31万人
  • “饿了么”枣庄一站点两名连襟骑手先后猝死,软件显示生前3天每日工作超11小时
  • 尹锡悦涉嫌发动内乱案举行第三次庭审