日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

数据结构杂谈番外篇——搞懂递归的小文章

發布時間:2023/12/9 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 数据结构杂谈番外篇——搞懂递归的小文章 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1 難題
    • 2 遞歸
      • 2.1 n的階層
      • 2.2 斐波那契數列的第n項
      • 2.3 逆序打印數組
    • 3 反轉鏈表
    • 4 回顧遞歸

1 難題

如果不想聽我談學習的過程而注重怎么學習,可以直接跳到第二小節

這個遞歸的問題是在我刷題的時候遇到的。事實上,我對遞歸是一竅不通的,第一次學遞歸是在大二上學期學的數據分析和可視化中遇到了,但是那時候老師叫我們背,所以沒怎么注意這個問題。

沒注意的問題在后面就開始暴露出來了。在Leetcode刷題的時候,第一次遇見遞歸是在反轉單鏈表的時候。反轉單鏈表一文可以在每日一題——劍指 Offer24反轉鏈表_塵魚好美的小屋-CSDN博客中查看。在當時,我用的僅僅是新手都能接受的迭代法。而無法接受思維混亂的遞歸,但是在解決Leetcode上的另外一道題的時候就開始出問題了,這道題必須用到遞歸或者棧。

我們學過棧的都知道,棧的本質是遞歸,這就意味著這個知識點是一個跨不過的坎,我知道我必須面對了。

對于解決這個問題,我首先是看了一下大佬的遞歸解法劍指 Offer 24. 反轉鏈表(迭代 / 遞歸,清晰圖解) - 反轉鏈表 - 力扣(LeetCode) (leetcode-cn.com)。但是我發現其對于遞歸的本質沒有詳細的闡述,反而是只提解法,這對于新手顯然十分不友好。我又在看不懂遞歸的看過來,希望能幫到你! - 反轉鏈表 - 力扣(LeetCode) (leetcode-cn.com)上面看到了另外一個大佬的解法,雖然講的挺好,但是在單鏈表反轉中又是讓人無法接受了,但是至此,我突然腦路一開,發現了一種新思路,我十分愿意和你分享我思考的思路,希望你耐心看完我的文章。

2 遞歸

大多數講述遞歸都是先引出斐波那契數列。實際上,我們無需畏懼遞歸這個名詞,我們先用另外一個詞來體會遞歸,即遞推公式,這在我們高中數學中幾乎人人學過。在講述斐波那契數列前,我們來解決一個問題。如何解決用遞歸實現n的階層計算?

2.1 n的階層

完成遞歸實際上就是三部曲:

  • 明確函數目的
  • 尋找遞歸結束條件
  • 找出函數的等價關系式

這么說好像太空了,我們來給出一個圖,實際上這個圖就是遞歸。

沒錯,我們可以把俄羅斯套娃看成是遞歸,也就是說,每一層的娃都是在解決問題,遞歸的過程是把解決的問題都留在最后,先從外到里一步一步取娃,然后在最里面的娃從里到外解決問題。

現在我們看往例子:如果我們要解決階層問題,那么結束遞歸的條件就是一個你知道的數,比如你從5的階層,那么自然1的階層是你知道的,那你就可以把1作為結束條件。當然,2你也知道是多少,甚至于更高。我們先用1來作為結束條件:

//結束條件 if(n == 1) {return 1; }

那我們接下來就是要寫函數等價式了,這實際上是一個創造套娃的過程,從最小的娃開始,和相鄰的娃建立聯系。也就是說,我們只關注第n個娃和n-1個娃之間的關系,在這個例子中,它們的關系就是f(n) = n*f(n-1)。

等價關系式的尋找在這里看起來十分簡單,可實際上,遞歸最難的就是此步。

接下來我們把上述寫成代碼,如下所示:

int func(int n) {if(n == 1)return 1;return n*func(n-1); }

也可以用2為結束遞歸條件,如下所示:

int func(int n) {if(n <= 2)return 2; //2的階層return n*func(n-1); }

綜上所述,這就是一個最簡單的遞歸了。在下面,我們層層遞進,來解決一些實際的問題。

2.2 斐波那契數列的第n項

我們來解決這么一個問題:

斐波那契數列的是這樣一個數列:1、1、2、3、5、8、13、21、34…,即第一項 f(1) = 1,第二項 f(2) = 1…,第 n 項目為 f(n) = f(n-1) + f(n-2)。求第 n 項的值是多少。

按照上面的套路,函數實際上是要返回一個第n項的值,所以我們可以這么定義:

int func(int n) {}

接下來我們要尋找遞歸結束的條件,根據題意,我們知道f(1) = 1,f(2) = 1… 那么根據我們在最開始講到的,我們實際上是尋求函數關系等價式,在本題中,如果你采用n = 1作為遞歸結束條件,那么在函數等價式中(本題已給出)有一個f(n) = f(n-1)+f(n-2),你把2填進去,會出現一個f(0),這樣的話越過了f(0)越過了遞歸結束條件n = 1,會無限死循環下去。

這顯然是我們不希望的,所以我們可以用n<=2來作為循環結束條件,這樣,f(n)中的n只能填3以上的數字才會出現循環。

綜上所述,代碼如下所示:

int func(int n){if(n <= 2){return 1;}return func(n-1) + func(n-2) }

2.3 逆序打印數組

上面的斐波那契數列問題實際上很容易看出函數等價關系式,讓我們來一個不那么明顯地例子。

我們需要逆序打印一個長度為n的數組。請問如何解決?

也就是說,我們要的是打印一個數組?我們可以這么做:

int func(int arrs[]) {}

接下來我們需要尋找遞歸結束條件,這個結束的條件就是數組為空,即n = 0就結束。

int func(int arrs[]) {if(n == 0)return false; }

現在讓我們來找函數等價關系,在這里,我們明顯要倒序打印,所以首要任務是先打印再倒推,倒推的過程實際上是一個類似于指針移動的過程:n = n-1,所以我們可以寫出如下代碼:

void func(int arrs[], int n) {if (n <= 0)return;cout << arrs[n - 1] << endl;return func(arrs, n - 1); }

3 反轉鏈表

回到我們的主題,我們要解決的最終問題是,如何解決反轉鏈表,乃至解決更多問題,既然要使用遞歸這個工具乘風破浪,那就先拿這道破題開刀吧。

定義一個函數,輸入一個鏈表的頭節點,反轉該鏈表并輸出反轉后鏈表的頭節點。

示例:

輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL

限制:

0 <= 節點個數 <= 5000

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof
著作權歸領扣網絡所有。商業轉載請聯系官方授權,非商業轉載請注明出處。

采用遞歸,首先要明白函數要干嘛,函數要反轉鏈表并且返回頭結點。

//逆置鏈表函數 ListNode* reverseList(ListNode * head) {}

但是實際上,我們要實現遞歸的地方不是在這個逆置函數內,所以我們可以另外寫一個遞歸函數。我們的思路是,分別指定兩根指針,一根為pre,一根為cur,反轉鏈表后cur.next = pre。最開始pre一定是空,cur一定是處于head的位置。

//逆置鏈表函數 ListNode* reverseList(ListNode * head) {}//遞歸函數 ListNode* recur(ListNode* cur, ListNode* pre) {}

接下來找結束條件。最開始pre是空,cur處于head的位置。當遞歸執行時,最內層的循環是cur快跑到null了。所以結束遞歸的條件一定是cur->next = NULL。

在最內層循環中,我們做的是:改變指針指向,即cur.next = pre。并且在最內層循環是,cur所處位置恰好是逆置后鏈表頭結點所處位置。當遞歸函數執行完成,返回逆置鏈表頭結點位置。而在逆置鏈表函數中,僅僅需要調用遞歸函數并且返回逆置鏈表頭結點位置即可。

class Solution { public:ListNode* reverseList(ListNode* head) {return recur(head, nullptr); // 調用遞歸并返回} private:ListNode* recur(ListNode* cur, ListNode* pre) {if (cur == nullptr) return pre; // 終止條件ListNode* res = recur(cur->next, cur); // 遞歸后繼節點cur->next = pre; // 修改節點引用指向return res; // 返回反轉鏈表的頭節點} };

從套娃的角度來看,我們可以這樣做:

4 回顧遞歸

遞歸實際上是一個解決子問題的過程。我們要解決f(n),實際上首先解決f(n-1),要解決f(n-1),實際上要先解決f(n-2),以此類推直至先解決最根本的問題,再回溯整個過程。

遞歸實際上也是需要優化的,比如f(n) = f(n-1)+f(n-2),如果n = 5,那么n-1 = 4,n-2 = 3,后續在遞歸的過程中會出現多次f(4)、f(3)等,如果每次都計算,開銷挺大,一般可以用某個值來保存,但是這篇文章是針對像我一樣的初學者的,我們就偷個懶,放自己一馬吧。

好了,彥祖,別太累了,好好消化一下就休息吧。

總結

以上是生活随笔為你收集整理的数据结构杂谈番外篇——搞懂递归的小文章的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。