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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

[递归]递归问题解题思路

發(fā)布時(shí)間:2023/12/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [递归]递归问题解题思路 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

遞歸解題三部曲

何為遞歸?程序反復(fù)調(diào)用自身即是遞歸。

我自己在剛開(kāi)始解決遞歸問(wèn)題的時(shí)候,總是會(huì)去糾結(jié)這一層函數(shù)做了什么,它調(diào)用自身后的下一層函數(shù)又做了什么…然后就會(huì)覺(jué)得實(shí)現(xiàn)一個(gè)遞歸解法十分復(fù)雜,根本就無(wú)從下手。

相信很多初學(xué)者和我一樣,這是一個(gè)思維誤區(qū),一定要走出來(lái)。既然遞歸是一個(gè)反復(fù)調(diào)用自身的過(guò)程,這就說(shuō)明它每一級(jí)的功能都是一樣的,因此我們只需要關(guān)注一級(jí)遞歸的解決過(guò)程即可。

如上圖所示,我們需要關(guān)心的主要是以下三點(diǎn):

  • 整個(gè)遞歸的終止條件。
  • 一級(jí)遞歸需要做什么?
  • 應(yīng)該返回給上一級(jí)的返回值是什么?
  • 因此,也就有了我們解遞歸題的三部曲:

  • 找整個(gè)遞歸的終止條件:遞歸應(yīng)該在什么時(shí)候結(jié)束?
  • 找返回值:應(yīng)該給上一級(jí)返回什么信息?
  • 本級(jí)遞歸應(yīng)該做什么:在這一級(jí)遞歸中,應(yīng)該完成什么任務(wù)?
  • 一定要理解這3步,這就是以后遞歸秒殺算法題的依據(jù)和思路。

    遞歸的三大要素

    第一要素:明確你這個(gè)函數(shù)想要干什么

    對(duì)于遞歸,我覺(jué)得很重要的一個(gè)事就是,這個(gè)函數(shù)的功能是什么,他要完成什么樣的一件事,而這個(gè),是完全由你自己來(lái)定義的。也就是說(shuō),我們先不管函數(shù)里面的代碼什么,而是要先明白,你這個(gè)函數(shù)是要用來(lái)干什么。

    第二要素:尋找遞歸結(jié)束條件

    所謂遞歸,就是會(huì)在函數(shù)內(nèi)部代碼中,調(diào)用這個(gè)函數(shù)本身,所以,我們必須要找出遞歸的結(jié)束條件,不然的話,會(huì)一直調(diào)用自己,進(jìn)入無(wú)底洞。也就是說(shuō),我們需要找出當(dāng)參數(shù)為啥時(shí),遞歸結(jié)束,之后直接把結(jié)果返回,請(qǐng)注意,這個(gè)時(shí)候我們必須能根據(jù)這個(gè)參數(shù)的值,能夠直接知道函數(shù)的結(jié)果是什么。

    第三要素:找出函數(shù)的等價(jià)關(guān)系式

    第三要素就是,我們要不斷縮小參數(shù)的范圍,縮小之后,我們可以通過(guò)一些輔助的變量或者操作,使原函數(shù)的結(jié)果不變。

    ?

    例1:求二叉樹(shù)的最大深度

    先看一道簡(jiǎn)單的Leetcode題目: Leetcode 104. 二叉樹(shù)的最大深度

    題目很簡(jiǎn)單,求二叉樹(shù)的最大深度,那么直接套遞歸解題三部曲模版:

  • 找終止條件。 什么情況下遞歸結(jié)束?當(dāng)然是樹(shù)為空的時(shí)候,此時(shí)樹(shù)的深度為0,遞歸就結(jié)束了。
  • 找返回值。 應(yīng)該返回什么?題目求的是樹(shù)的最大深度,我們需要從每一級(jí)得到的信息自然是當(dāng)前這一級(jí)對(duì)應(yīng)的樹(shù)的最大深度,因此我們的返回值應(yīng)該是當(dāng)前樹(shù)的最大深度,這一步可以結(jié)合第三步來(lái)看。
  • 本級(jí)遞歸應(yīng)該做什么。 首先,還是強(qiáng)調(diào)要走出之前的思維誤區(qū),遞歸后我們眼里的樹(shù)一定是這個(gè)樣子的,看下圖。此時(shí)就三個(gè)節(jié)點(diǎn):root、root.left、root.right,其中根據(jù)第二步,root.left和root.right分別記錄的是root的左右子樹(shù)的最大深度。那么本級(jí)遞歸應(yīng)該做什么就很明確了,自然就是在root的左右子樹(shù)中選擇較大的一個(gè),再加上1就是以root為根的子樹(shù)的最大深度了,然后再返回這個(gè)深度即可。
  • 具體代碼如下:

    # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object):def maxDepth(self, root):""":type root: TreeNode:rtype: int"""if root is None:return 0left_height = self.maxDepth(root.left)right_height = self.maxDepth(root.right)return max(left_height, right_height) + 1

    ?

    例2:兩兩交換鏈表中的節(jié)點(diǎn)

    看了一道遞歸套路解決二叉樹(shù)的問(wèn)題后,有點(diǎn)套路搞定遞歸的感覺(jué)了嗎?我們?cè)賮?lái)看一道Leetcode中等難度的鏈表的問(wèn)題,掌握套路后這種中等難度的問(wèn)題真的就是秒:Leetcode 24. 兩兩交換鏈表中的節(jié)點(diǎn)

    直接上三部曲模版:

  • 找終止條件。 什么情況下遞歸終止?沒(méi)得交換的時(shí)候,遞歸就終止了唄。因此當(dāng)鏈表只剩一個(gè)節(jié)點(diǎn)或者沒(méi)有節(jié)點(diǎn)的時(shí)候,自然遞歸就終止了。
  • 找返回值。 我們希望向上一級(jí)遞歸返回什么信息?由于我們的目的是兩兩交換鏈表中相鄰的節(jié)點(diǎn),因此自然希望交換給上一級(jí)遞歸的是已經(jīng)完成交換處理,即已經(jīng)處理好的鏈表。
  • 本級(jí)遞歸應(yīng)該做什么。 結(jié)合第二步,看下圖!由于只考慮本級(jí)遞歸,所以這個(gè)鏈表在我們眼里其實(shí)也就三個(gè)節(jié)點(diǎn):head、head.next、已處理完的鏈表部分。而本級(jí)遞歸的任務(wù)也就是交換這3個(gè)節(jié)點(diǎn)中的前兩個(gè)節(jié)點(diǎn),就很easy了。
  • 附上代碼:

    # Definition for singly-linked list. # class ListNode(object): # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution(object):def swapPairs(self, head):""":type head: ListNode:rtype: ListNode"""#終止條件:鏈表只剩一個(gè)節(jié)點(diǎn)或者沒(méi)節(jié)點(diǎn)了,沒(méi)得交換了。返回的是已經(jīng)處理好的鏈表if head is None or head.next is None:return head#一共三個(gè)節(jié)點(diǎn):head, next, swapPairs(next.next)#下面的任務(wù)便是交換這3個(gè)節(jié)點(diǎn)中的前兩個(gè)節(jié)點(diǎn)newHead = head.nexthead.next = self.swapPairs(newHead.next) newHead.next = head#根據(jù)第二步:返回給上一級(jí)的是當(dāng)前已經(jīng)完成交換后,即處理好了的鏈表部分return newHead

    ?

    例3:反轉(zhuǎn)單鏈表

    206. 反轉(zhuǎn)鏈表

    反轉(zhuǎn)單鏈表。例如鏈表為:1->2->3->4。反轉(zhuǎn)后為 4->3->2->1

    1. 定義遞歸函數(shù)功能

    假設(shè)函數(shù) reverseList(head) 的功能是反轉(zhuǎn)但鏈表,其中 head 表示鏈表的頭節(jié)點(diǎn)

    2.尋找結(jié)束條件

    當(dāng)鏈表只有一個(gè)節(jié)點(diǎn),或者如果是空表的話,你應(yīng)該知道結(jié)果吧?直接啥也不用干,直接把 head 返回

    3. 尋找等價(jià)關(guān)系

    它的等價(jià)條件中,一定是范圍不斷在縮小,對(duì)于鏈表來(lái)說(shuō),就是鏈表的節(jié)點(diǎn)個(gè)數(shù)不斷在變小,所以,如果你實(shí)在找不出,你就先對(duì) reverseList(head.next) 遞歸走一遍,看看結(jié)果是咋樣的。

    先縮小范圍,先對(duì) 2->3->4遞歸下試試,即代碼如下

    class Solution:def reverseList(self, head: ListNode) -> ListNode:if not head or not head.next:return headnewHead = self.reverseList(head.next)

    們?cè)诘谝徊降臅r(shí)候,就已經(jīng)定義了 reverseLis t函數(shù)的功能可以把一個(gè)單鏈表反轉(zhuǎn),所以,我們對(duì) 2->3->4反轉(zhuǎn)之后的結(jié)果應(yīng)該是這樣:

    我們把 2->3->4 遞歸成 4->3->2。不過(guò),1 這個(gè)節(jié)點(diǎn)我們并沒(méi)有去碰它,所以 1 的 next 節(jié)點(diǎn)仍然是連接這 2。

    接下來(lái)呢?該怎么辦?

    其實(shí),接下來(lái)就簡(jiǎn)單了,我們接下來(lái)只需要把節(jié)點(diǎn) 2 的 next 指向 1,然后把 1 的 next 指向 null,不就行了?,即通過(guò)改變 newList 鏈表之后的結(jié)果如下:

    也就是說(shuō),reverseList(head) 等價(jià)于 reverseList(head.next) + 改變一下1,2兩個(gè)節(jié)點(diǎn)的指向。好了,等價(jià)關(guān)系找出來(lái)了,代碼如下(有詳細(xì)的解釋):

    class Solution:def reverseList(self, head: ListNode) -> ListNode:if not head or not head.next:return headnewHead = self.reverseList(head.next)#改變 1,2節(jié)點(diǎn)的指向。#通過(guò) head.next獲取節(jié)點(diǎn)2t1 = head.next#讓 2 的 next 指向 2t1.next = head#1 的 next 指向 null.head.next = None#把調(diào)整之后的鏈表返回。return newHead

    ?

    例4:平衡二叉樹(shù)

    那么請(qǐng)你先不看以下部分,嘗試解決一下這道easy難度的Leetcode題(個(gè)人覺(jué)得此題比上面的medium難度要難):Leetcode 110. 平衡二叉樹(shù)

    我覺(jué)得這個(gè)題真的是集合了模版的精髓所在,下面套三部曲模版:

  • 找終止條件。 什么情況下遞歸應(yīng)該終止?自然是子樹(shù)為空的時(shí)候,空樹(shù)自然是平衡二叉樹(shù)了。

  • 應(yīng)該返回什么信息:

    為什么我說(shuō)這個(gè)題是集合了模版精髓?正是因?yàn)榇祟}的返回值。要知道我們搞這么多花里胡哨的,都是為了能寫(xiě)出正確的遞歸函數(shù),因此在解這個(gè)題的時(shí)候,我們就需要思考,我們到底希望返回什么值?

    何為平衡二叉樹(shù)?平衡二叉樹(shù)即左右兩棵子樹(shù)高度差不大于1的二叉樹(shù)。而對(duì)于一顆樹(shù),它是一個(gè)平衡二叉樹(shù)需要滿足三個(gè)條件:它的左子樹(shù)是平衡二叉樹(shù),它的右子樹(shù)是平衡二叉樹(shù),它的左右子樹(shù)的高度差不大于1。換句話說(shuō):如果它的左子樹(shù)或右子樹(shù)不是平衡二叉樹(shù),或者它的左右子樹(shù)高度差大于1,那么它就不是平衡二叉樹(shù)。

    而在我們眼里,這顆二叉樹(shù)就3個(gè)節(jié)點(diǎn):root、left、right。那么我們應(yīng)該返回什么呢?如果返回一個(gè)當(dāng)前樹(shù)是否是平衡二叉樹(shù)的boolean類(lèi)型的值,那么我只知道left和right這兩棵樹(shù)是否是平衡二叉樹(shù),無(wú)法得出left和right的高度差是否不大于1,自然也就無(wú)法得出root這棵樹(shù)是否是平衡二叉樹(shù)了。而如果我返回的是一個(gè)平衡二叉樹(shù)的高度的int類(lèi)型的值,那么我就只知道兩棵樹(shù)的高度,但無(wú)法知道這兩棵樹(shù)是不是平衡二叉樹(shù),自然也就沒(méi)法判斷root這棵樹(shù)是不是平衡二叉樹(shù)了。

  • 本級(jí)遞歸應(yīng)該做什么。 知道了第二步的返回值后,這一步就很簡(jiǎn)單了。目前樹(shù)有三個(gè)節(jié)點(diǎn):root,left,right。我們首先判斷l(xiāng)eft子樹(shù)和right子樹(shù)是否是平衡二叉樹(shù),如果不是則直接返回false。再判斷兩樹(shù)高度差是否不大于1,如果大于1也直接返回false。否則說(shuō)明以root為節(jié)點(diǎn)的子樹(shù)是平衡二叉樹(shù),那么就返回true和它的高度。

  • ?

    判斷左右子樹(shù)的高度差是否超過(guò)1,依次遞歸

    # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(object):def isBalanced(self, root):""":type root: TreeNode:rtype: bool"""if root is None:return Trueif abs(self.getHeight(root.left) - self.getHeight(root.right)) > 1:return Falsereturn self.isBalanced(root.left) and self.isBalanced(root.right)def getHeight(self, node):if node is None:return 0return max(self.getHeight(node.left), self.getHeight(node.right)) + 1

    ?

    ?

    ?

    一些可以用這個(gè)套路解決的題

    暫時(shí)就寫(xiě)這么多啦,作為一個(gè)高考語(yǔ)文及格分,大學(xué)又學(xué)了工科的人,表述能力實(shí)在差因此啰啰嗦嗦寫(xiě)了一大堆,希望大家能理解這個(gè)很好用的套路。

    下面我再列舉幾道我在刷題過(guò)程中遇到的也是用這個(gè)套路秒的題,真的太多了,大部分鏈表和樹(shù)的遞歸題都能這么秒,因?yàn)闃?shù)和鏈表天生就是適合遞歸的結(jié)構(gòu)。

    我會(huì)隨時(shí)補(bǔ)充,正好大家可以看了上面三個(gè)題后可以拿這些題來(lái)練練手,看看自己是否能獨(dú)立快速準(zhǔn)確的寫(xiě)出遞歸解法了。

    Leetcode 101. 對(duì)稱二叉樹(shù)

    Leetcode 111. 二叉樹(shù)的最小深度

    Leetcode 226. 翻轉(zhuǎn)二叉樹(shù)

    Leetcode 617. 合并二叉樹(shù)

    Leetcode 654. 最大二叉樹(shù)

    Leetcode 83. 刪除排序鏈表中的重復(fù)元素

    ?

    https://mp.weixin.qq.com/s/PgSSYc50ajnbh8zD6Ei07g

    https://mp.weixin.qq.com/s/mJ_jZZoak7uhItNgnfmZvQ

    https://lyl0724.github.io/2020/01/25/1/

    總結(jié)

    以上是生活随笔為你收集整理的[递归]递归问题解题思路的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。