【C/C++算法】藍橋杯之遞歸算法(如何編寫想出遞歸寫法)

緒論:沖擊藍橋杯一起加油!!
在這里插入圖片描述
每日激勵:“不設限和自我肯定的心態:I can do all things。 — Stephen Curry”

緒論?:
————————
早關注不迷路,話不多說安全帶系好,發車啦(建議電腦觀看)。


遞歸

1. 什么是遞歸

簡單來說:就是函數自己調用自己

2. 為什么會用到遞歸

常見的遞歸有:二叉樹的遍歷、快排、歸并
在這里插入圖片描述

其中遞歸的本質:

  1. 解決主問題,衍生相同的子問題
  2. 在處理子問題的時候,又出現了相同的子問題
  3. 所以本質就是不斷自己調用自己,通過縮小問題最終解決最小子問題

3. 如何理解遞歸(非常重要!)

對于理解遞歸,可以從下面三個方向理解:

  1. 遞歸展開的細節圖
  2. 二叉樹中的題目
  3. 宏觀看待遞歸的過程
    1. 不要在意遞歸的展開圖
    2. 把遞歸的函數當成一個黑盒(具體里面如何操作關心,給他數據,返回結果)
    3. 相信這個黑盒一定能完成這個任務

(這個后面慢慢的到來,請繼續往后看)

4. 如何寫好一個遞歸?

  1. 先找到相同的子問題
    1. 根據子問題:決定了函數頭的設計
    2. 本質也就是分析題目,然后得出解決子問題可能需要用到的參數(一般來說可以先粗力度的得到一個函數頭,然后再在編寫代碼中不斷彌補)
      在這里插入圖片描述
  2. 只關心某個子問題是如何解決的
    1. 把他看成一個黑盒,并相信他能夠替你完成任務
    2. 也就決定了函數體的書寫,我們僅僅去想某個子問題的解決方法
    3. 因為這樣再次調用該函數的時候,雖然條件參數可能不同,但子問題的解決方法是一致的
      在這里插入圖片描述
  3. 最后再注意一下遞歸函數的出口即可
    1. 也就是防止無限遞歸的情況

總結:解決簡單遞歸的三步:

非常重要,不過通過大量練習相信你就能很好的理解(可能初次看會迷糊~)

  1. 通過題目寫出dfs的函數頭
  2. 根據子問題寫出函數內部邏輯
  3. 注意一下遞歸出口

深度優先遍歷 vs 寬度優先遍歷 vs 暴搜

  1. 深度優先遍歷(搜索)dfs(depth):

    1. 通俗的來說就是:一條路走到黑
      在這里插入圖片描述
  2. 寬度優先遍歷(搜索)bfs(Breadth):

    1. 本質也就是:一層一層的遍歷樹
      在這里插入圖片描述
  3. 搜索(暴搜)

    1. 本質就是暴力枚舉一遍所有的情況,或者說就是將樹中的所有節點都進行一次遍歷
  4. 搜索問題的拓展:

拿全排列問題舉例:
全排列是將一組數中的所有情況排出來(如下圖)
在這里插入圖片描述
那么可以使用樹狀圖(決策樹)的方式解決具體如下圖
在這里插入圖片描述
此時畫出來后,我們需要的答案最終就能通過遞歸搜索的方式獲取到:
在這里插入圖片描述
所以說我們不能對于dfs、bfs來說局限于二叉樹,而是當我們能畫出類似的決策樹的形式畫出來那么就能使用dfs/bfs

回溯與剪枝(非常重要!)

  1. 回溯的本質:其實就是深搜中當我們嘗試某種情況時,發現這種情況行不通,退回到上一級的操作就是回溯
    (如下圖紅線)
    在這里插入圖片描述
  2. 剪枝來說:在我們回溯過程后可能遇到兩種可以走的情況,而其中一種情況已經走過了知道行不通,那么這條路就代表被剪枝了
    (具體如下圖)
    在這里插入圖片描述

下面將通過5道題目帶你理解遞歸,其中注意理解遞歸的三步(函數頭,函數體,遞歸出口)


具體訓練:

1. 漢諾塔

題目:

在這里插入圖片描述

分析題目并提出,解決方法:

在這里插入圖片描述
在這里插入圖片描述
在過程中不允許大盤子摞在小盤子上面!

題解核心邏輯:

漢諾塔問題,可以用遞歸解決
如何來解決漢諾塔問題?
在這里插入圖片描述

  1. 在N >= 2 時,我們要考慮先將最大的盤子放到 C柱上

問題就轉移成了:

  1. 先把最大盤子上面的所有先放在B柱上,然后在移動 最大的盤子 到C柱
    在這里插入圖片描述
  2. 第三步就是將所有B柱上的盤子在移動到C柱上
    在這里插入圖片描述
  3. 再次理解:當N = 3時,看最上面的兩個盤子,它的本質其實就是N = 2的情況,只不過這次是移動到B柱
    在這里插入圖片描述
  4. 這里注意理解的是:操作1和操作3的本質是一樣的,只不過借助的柱子不一樣!
  5. 那么 N = … 他們的本質都是一樣了,他們都是:
    1. 執行步驟1,將最大盤子上面的小盤子全部轉移到B
    2. 執行步驟2,將最大盤子全部轉移到C
    3. 執行步驟3,再將B上的轉移到C即可完成
    4. 所以為什么可以用遞歸:解決大問題的時候,出現了相同的子問題,解決子問題的時候又出現了相同的子問題
      在這里插入圖片描述

如何寫代碼:

  1. 挖掘重復子問題(函數頭)
    1. 也就是上面的 步驟 1、 2、3所需要的
    2. 該題本質是:將 x 柱上的一堆盤子,借助 y 柱子,轉移到 z 柱子上
    3. 那么函數頭也就如下圖
      在這里插入圖片描述
  2. 只關心某一個子問題在做什么(函數體)
    1. 宏觀的分析三個步驟的具體:
      在這里插入圖片描述
  3. 函數出口
    1. 不難發現就是 N = 1 的時候是不同的,直接將A柱的盤子直接放到C柱上
    2. 那么當N=1時,將A柱的盤子直接放到C柱上,然后就可以退出了
      在這里插入圖片描述
      具體步驟代碼如下:
class Solution {
public:
//1. 挖掘重復子問題(得出函數頭)
//將n個盤子移動到 借助柱子移動到目標柱子void h(vector<int>& A, vector<int>& B, vector<int>& C,int n){//出口:if(n == 1){// 將 A 上的移動到 C上C.push_back(A.back());A.pop_back();return ;}//2. 分析子問題:(得到函數體所需要的操作,并且相信他能完成)
//先將 A 柱上的 n-1 個移動到 B盤h(A,C,B,n-1);
//將 A 中的最后一個盤子移動到 C上C.push_back(A.back());A.pop_back();
//在將B上的盤子借助A全部移動到C上h(B,A,C,n-1);} void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {h(A,B,C,A.size());}
};

2. 合并兩個有序鏈表

題目:

在這里插入圖片描述

分析題目并提出,解決方法:

題目很好理解:就是拼接兩個鏈表,過程中不允許創建空間

分析本題查看是否有重復子問題:
在這里插入圖片描述
在這里插入圖片描述

  1. 重復子問題(函數頭的設計)

    1. 合并兩個有序鏈表
    2. 那么也就僅需要兩個 鏈表
      在這里插入圖片描述
  2. 只關心某個子問題(函數體的設計)
    在這里插入圖片描述

    1. 比大小(兩個鏈表進行比較)得到較小的結點
    2. 將較小的結點看做合并后的鏈表頭結點(通過修改該結點的next指針完成)
      在這里插入圖片描述
    3. 最終返回 較小的結點
  3. 遞歸出口

    1. 那個指針先為空返回另外一個指針

總結:遞歸 = 重復子問題 + 宏觀看待遞歸問題

題解核心邏輯:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:
//分析函數的目的:合并兩個鏈表:所以函數頭就是 兩個鏈表即可ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
//編寫出口:判斷那個鏈表先為空,返回另外一個if(list1 == nullptr) return list2;if(list2 == nullptr) return list1;//編寫子問題的具體操作:完成函數體//1. 找到較小的if(list1->val <= list2->val){list1->next =  mergeTwoLists(list1->next,list2);return list1; }else{list2->next =  mergeTwoLists(list1,list2->next);return list2; }}
};

小總結:

遞歸 VS 深搜

遞歸的展開圖,其實就是對一棵樹做一次深度優先遍歷(dfs)
而在遞歸的過程中需要一個 進行保存,歷史數據
在這里插入圖片描述

循環(迭代) vs 遞歸

它們是能相互轉換的,那么什么時候,用哪一個呢?
在這里插入圖片描述
通過上面的分析和上圖理解到:

  1. 當我們的一個遍歷過程需要用到類似棧的東西進行保存數據時,就是比較麻煩的情況了,此時我們使用遞歸的形式就能很簡單方便的寫出程序
  2. 而當一些遍歷過程比較簡單,如上圖右邊結構,此時遍歷僅僅只需要單方向的那么,此時就沒必要使用遞歸,因為一個簡單的遍歷循環即可完成
    在這里插入圖片描述

3. 反轉鏈表

題目:

在這里插入圖片描述

分析題目并提出,解決方法:

宏觀角度看待問題:

  1. 讓當前結點后面的鏈表先逆序,并且把頭結點返回
    在這里插入圖片描述
  2. 讓當前結點添加到逆置后的鏈表在這里插入圖片描述

第二個視角:將鏈表看成一顆樹:

  1. 不斷深度遍歷到最后一個結點
  2. 返回最后一個結點
  3. 到達倒數第二層:執行將自己下一個結點的next置為自己,然后自己置為null(這里置為是為了保持所以子操作一致)
    在這里插入圖片描述
    在這里插入圖片描述
    在這里插入圖片描述

題解核心邏輯:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {//使用遞歸的方式//宏觀的看待問題://翻轉鏈表(所以函數頭就只需要一個鏈表即可,還需要返回新頭結點)//子問題(將自己的next的next置為自己,再將自己置為空,并且還需要將最后的頭結點返回回來)//出口:(當head為空時退出,表示遍歷到了最后結點)//head == nullptr 時為了防止沒有結點的情況if(head == nullptr || head->next == nullptr){return head;//返回結點}ListNode* newhaed = reverseList(head->next);head->next->next = head;head->next = nullptr;return newhaed;}
};

4. 兩兩交換鏈表中的節點

題目:

在這里插入圖片描述

分析題目并提出,解決方法:

遞歸思想(宏觀看待):

  1. 分析題目得到遞歸思想:將給到的鏈表中的兩兩進行交換順序,那么也就僅需要一個鏈表參數即可
    在這里插入圖片描述
  2. 看待某個子問題:
    1. dfs會返回一個新的鏈表的結點,使用tmp記錄
    2. 將當前head的next的next指向haed,再將head的next指向tmp(完成交換的目的)
      在這里插入圖片描述
  3. 退出情況,當head為空是退出
    1. 注意其中是以兩個結點看成一起的,所以退出條件是:
    2. 當 head為空 || head->next 為空

題解核心邏輯:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* swapPairs(ListNode* head) {//退出條件:if(head == nullptr || head->next == nullptr){return head;//返回當前結點}//子問題://1. 獲取dfs后面的頭結點ListNode* tmp = swapPairs(head->next->next);ListNode*  ret = head->next;ret->next = head;head->next = tmp;return ret;}
};

5. Pow(x, n)

題目:

在這里插入圖片描述

分析題目并提出,解決方法:

很好理解 就是 求x n等于多少
解法1:暴力循環,讓 x 乘 n 次即可(會超時)

解法二:

快速冪

快速冪的實現:

  1. 遞歸實現
  2. 循環實現

本題將遞歸實現:
例子:當我們要求 316

  • 就是將 316 不斷的對半看(具體如下圖)
    先求出 38 這樣 38 * 38 = 316 同理 38 = 34 * 34 。。。
    在這里插入圖片描述
  • 其中暴力解法要求 16 次,而使用這種方法只用求logn次
  • 附:當 n 為奇數時:多乘一個自身即可
    在這里插入圖片描述
  1. 相同子問題 -->函數頭
    • 求一個 x的n次冪 ==> pow(x,n)
  2. 只關心每個子問題做了什么 – > 函數體
    • 其中需要 判斷 n 的奇偶性
    • 通過遞歸獲取自身的值,并且判斷奇偶性得出是否要多乘1位
  3. 遞歸出口
    • 當 n == 0 時返回1(這樣上層 n = 1 處等于 1 * x(1 * 1 * x))
      在這里插入圖片描述
      特殊情況:
  • n 為負數:
    在這里插入圖片描述
  • n 可能是 -231 當變成整數就可能越界,所以得使用long long(整形的范圍是 -231 ~ 231 - 1
    在這里插入圖片描述

題解核心邏輯:

  • 其中注意 -(long long)n 這里的操作:它是將 n 的類型轉換成了long long(并且不能寫在-前面,只能挨著n
  • 只有這樣當 n = -231 時強轉為正數后就不會溢出
class Solution {
public:double myPow(double x, int n) {//遞歸實現://將 x^n 看成 x^(n/2) * x^(n/2) ...return n >= 0 ? pow(x,n) : 1/pow(x,-(long long)n);}double pow(double x,long long n){if(n == 0) return 1;double tmp= myPow(x,n/2);return n % 2 == 0 ? tmp * tmp : tmp * tmp * x;}
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76758.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76758.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76758.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[ctfshow web入門] web5

前置知識 引用博客&#xff1a;phps的利用 當服務器配置了 .phps 文件類型時&#xff0c;訪問 .phps 文件會以語法高亮的形式直接顯示 PHP 源代碼&#xff0c;而不是執行它。.phps被作為輔助開發者的一種功能&#xff0c;開發者可以通過網站上訪問xxx.phps直接獲取高亮源代碼 …

day 8 TIM定時器

一、STM32 定時器概述 1. 定時器的概述定時器的基本功能&#xff0c;但是 STM32 的定時器除了具有定時功能之外&#xff0c;也具有定時器中斷功能&#xff0c;還具有輸入捕獲&#xff08;檢測外部信號&#xff09;以及輸出比較功能&#xff08;輸出不同的脈沖&#xff09;&…

Spring Boot 中使用 Redis:從入門到實戰

&#x1f31f; 前言 歡迎來到我的技術小宇宙&#xff01;&#x1f30c; 這里不僅是我記錄技術點滴的后花園&#xff0c;也是我分享學習心得和項目經驗的樂園。&#x1f4da; 無論你是技術小白還是資深大牛&#xff0c;這里總有一些內容能觸動你的好奇心。&#x1f50d; &#x…

hi3516cv610通過menuconfig關閉的宏記錄

hi3516cv610通過menuconfig關閉的宏記錄 defconfig為 hi3516cv610_debug_defconfig或hi3516cv610_new_defconfig 1、 變為 2、 變為 3、 變為 4、 變為 5、 變為

WebSocket 詳解:構建一個復雜的實時聊天應用

文章目錄 一、前言二、WebSocket 基礎2.1 WebSocket 與 HTTP 的區別2.2 WebSocket 的優點 三、搭建 WebSocket 服務端3.1 安裝 ws 和 redis 庫3.2 創建 WebSocket 服務端3.3 創建用戶身份驗證 四、前端實現 WebSocket 客戶端4.1 創建 Vue 3 項目4.2 實現 WebSocket 連接和用戶注…

【JavaEE進階】Spring AOP入門

歡迎關注個人主頁&#xff1a;逸狼 創造不易&#xff0c;可以點點贊嗎 如有錯誤&#xff0c;歡迎指出~ AOP是Spring框架的第??核?(第??核?是 IoC) 什么是AOP&#xff1f; ? AspectOrientedProgramming&#xff08;?向切?編程&#xff09; 什么是?向切?編程呢? 切…

算法思想之雙指針(一)

歡迎拜訪&#xff1a;霧里看山-CSDN博客 本篇主題&#xff1a;算法思想之雙指針(一) 發布時間&#xff1a;2025.4.4 隸屬專欄&#xff1a;算法 目錄 雙指針算法介紹對撞指針&#xff1a;快慢指針&#xff1a; 例題移動零題目鏈接題目描述算法思路代碼實現 復寫零題目鏈接題目描…

【11408學習記錄】英語寫作黃金模板+語法全解:用FTC數據泄漏案掌握書信結構與長難句拆解(附思維導圖)

2025.04.04 英語寫作書信寫作第一段私人信件公務信函 語法總結——簡單句簡單句的核心&#xff1a;謂語動詞的變化詞性的拓展限定詞 形容詞與副詞介詞短語 成分的擴展同位語插入語 非謂語動詞 每日一句詞匯 第一步&#xff1a;辨別第二步&#xff1a;斷開第三步&#xff1a;簡化…

手機顯示5GA圖標的條件

最近有星友問在什么情況下才能顯示5G-A&#xff1f;雖然這個我也不知道&#xff0c;但是我有幾個運營商的5G終端白皮書&#xff0c;從上面就可以找到答案。 如上是幾個運營商顯示5G-A的條件&#xff0c;基本上考慮的都是3CC的情況&#xff0c;聯通還有考慮200M CA 2CC的場景&am…

網絡:華為數通HCIA學習:IP路由基礎

華為HCIA學習 IP路由基礎路由協議或路由種類以及對應路由的優先級按工作區域分類&#xff1a;按工作機制及算法分類&#xff1a;路由的優先級路由器選擇最優路由的順序是什么? 前言自治系統LAN和廣播域路由選路IP路由表路由度量建立路由表最長匹配原則路由器轉發數據包總結 IP…

Docker 鏡像相關的基本操作

一、Docker 鏡像基本操作 1. 查找鏡像 命令&#xff1a; docker search <鏡像名稱> 示例&#xff1a;查找 CentOS 鏡像&#xff1a; docker search centos 命令解釋&#xff1a; 默認從 Docker Hub 官方倉庫上搜索鏡像。搜索結果包含多個列&#xff1a; NAME&…

Linux文件特殊權限管理及進程和線程

acl 權限優先級 擁有者 > 特殊指定用戶 > 權限多的組 >權限少的組 > 其他 mask閾值 mask是能夠賦予指定用戶權限的最大閥值 當設定完畢文件的acl列表之后用chmod縮小了文件擁有組的權力 mask會發生變化 恢復&#xff1a; setfacl -m m: 權限 :rwx 文件/…

NVIDIA AgentIQ 詳細介紹

NVIDIA AgentIQ 詳細介紹 1. 引言 NVIDIA AgentIQ 是一個靈活的庫&#xff0c;旨在將企業代理&#xff08;無論使用何種框架&#xff09;與各種數據源和工具無縫集成。通過將代理、工具和代理工作流視為簡單的函數調用&#xff0c;AgentIQ 實現了真正的可組合性&#xff1a;一…

算法設計與分析5(動態規劃)

動態規劃的基本思想 將一個問題劃分為多個不獨立的子問題&#xff0c;這些子問題在求解過程中可能會有些數據進行了重復計算。我們可以把計算過的數據保存起來&#xff0c;當下次遇到同樣的數據計算時&#xff0c;就可以查表直接得到答案&#xff0c;而不是再次計算 動態規劃…

怎么理解量子比特模型,遷移到量子計算機開始編程

怎么理解量子比特模型&#xff0c;遷移到量子計算機開始編程 視頻鏈接&#xff1a; 好的現在是2025年的3月最后一天,3月31號,今天我們討論的話題是量子編程,也就是在量子計算機上,使用特定的語言進行軟件開發。當然我們要討論的,不是,量子編程的某一門語言的技術細節,而是考慮…

使用Expo框架開發APP——詳細教程

在移動應用開發日益普及的今天&#xff0c;跨平臺開發工具越來越受到開發者青睞。Expo 是基于 React Native 的一整套工具和服務&#xff0c;它能夠大幅降低原生開發的門檻&#xff0c;讓開發者只需關注業務邏輯和界面實現&#xff0c;而不用糾結于復雜的原生配置。本文將從零開…

windows技術基礎知識

NT架構 NT 就是new techonology 的英文單詞縮寫&#xff0c;是微軟1993年推出操作系統的重大升級&#xff0c;如內存管理&#xff0c;安全機制&#xff0c;多任務&#xff0c;多線程支持。在此之前操作系統都是基于MS-DOS上面的圖形化界面&#xff0c;只有有限的內存管理和多任…

迪杰斯特拉+二分+優先隊列+拓撲+堆優化(奶牛航線Cowroute、架設電話線dd、路障Roadblocks、奶牛交通Traffic)

原文地址 https://fmcraft.top/index.php/Programming/2025040402.html 主要算法 迪杰斯特拉Dijkstra 題目列表 P1&#xff1a;奶牛航線Cowroute 題目描述 題目描述 Bessie已經厭倦了農場冬天的寒冷氣候&#xff0c;她決定坐飛機去更溫暖的地方去度假。不幸的是&#xf…

#Liunx內存管理# 在32bit Linux內核中,用戶空間和內核空間的比例通常是3:1,可以修改成2:2嗎?

在32位Linux內核中&#xff0c;用戶空間和內核空間的3:1默認比例可以修改為2:2&#xff0c;但需要權衡實際需求和潛在影響。以下是具體分析&#xff1a; 一、修改可行性 1.技術實現 通過內核啟動參數調整虛擬地址空間劃分&#xff0c;例如在GRUB配置中添加mem2G參數&#xff0c…

JAVA:使用 Curator 進行 ZooKeeper 操作的技術指南

1、簡述 Apache Curator 是一個基于 ZooKeeper 的 Java 客戶端庫&#xff0c;它極大地簡化了使用 ZooKeeper 的開發工作。Curator 提供了高層次的 API&#xff0c;封裝了很多復雜的 ZooKeeper 操作&#xff0c;例如連接管理、分布式鎖、Leader 選舉等。 在分布式系統中&#…