【leetcode记录02】递归
(一)相關(guān)原理
1.減治思想:
? 在拆分子問題的時候,只將原問題轉(zhuǎn)化成 一個 規(guī)模更小的子問題,因此子問題的結(jié)果就是上一層原問題的結(jié)果,每一步只需要解決一個規(guī)模更小的子問題,相比較于「分治思想」而言,它 沒有「合并」的過程。
? 「減治思想」思想的典型應(yīng)用是「二分查找」「選擇排序」「插入排序」「快速排序」算法。
2.遞歸與迭代
2.1「自頂向下」與「遞歸」
? 「自頂向下」是直接面對我們要解決的問題,逐層拆分,直到不能拆分為止,再按照拆分的順序的逆序逐層解決,直至原問題得到了解決,這是「遞歸」。
2.2「自底向上」與「迭代」
? 如果我們非常清楚一個問題最開始的樣子,并且也清楚一個問題是如何從它最開始的樣子逐步演變成為我們想要求解的問題的樣子,我們就可以通過「遞推」的方式,從小規(guī)模的問題開始逐步「遞推」得到最終要解決的大問題的解。
(很關(guān)鍵一點(diǎn)是,使用迭代必須要 能正確且全面地抽取出其實(shí)行邏輯,否則的話可能會出現(xiàn)邏輯不完善,邊界條件不能完全考慮到等問題,可能會需要很多條件判斷等步驟,但是想起來可能更加符合人思考的邏輯。 )
(二)關(guān)鍵例題
1.歸并排序
? 使用了減治的思想,通過遞歸地二分操作來進(jìn)行處理,首先遞歸地將數(shù)組一分為二,然后沒有其他操作直到觸底,但是在回溯的過程中,需要進(jìn)行一個逐個檢驗(yàn)并合并數(shù)組的操作(這個過程實(shí)際是自底向上的),從數(shù)據(jù)形式上,傳入的始終都是數(shù)組形式。
def mergeSort(self,alist):#觸底條件if len(alist)<=1:return alist#遞歸主體 (基于下行過程中的二分操作,只是將它改成了遞歸式,從而可以在回溯的過程中進(jìn)行后面寫的那個操作)mid=len(alist)//2 #這里思考的時候一開始想到要分奇偶,而且每隔一個都會奇偶交替,但實(shí)際模擬一下發(fā)現(xiàn)并不需要分,畢竟就算不等分也沒關(guān)系,等分并不是硬性要求,最后都能順利觸底就好left=self.mergeSort(alist[:alist])right=self.mergeSort(alist[alist:])#回溯過程中的操作merged=[]while left and right:if left[0]<=right[0]: #這里一定程度上可以認(rèn)為是用了雙指針(但它僅僅是用了兩個指針而已)merged.append(left.pop(0)) #非常pythonic,所以不會真實(shí)地用到類似指針的東西else:merged.append(right.pop(0))merged.extend(left if left else right)return merged2.快速排序
①自己想的常方法(要用到額外空間)
? 也是使用了減治的思想,只是之前是按照序號進(jìn)行二分,這次選擇的是一個數(shù)字,然后按照數(shù)值大小來排,也是一種可實(shí)現(xiàn)的方法,遞歸實(shí)現(xiàn)。
def quickSort(self,alist):#觸底條件(遞歸結(jié)束條件)if len(alist)<=1:return alist#寫出每一次順利二分的操作a=alist[0] #選擇參考數(shù)值為最左側(cè)數(shù)據(jù)l=[]l.append(a)cnt=0for i in alist[1:]:if i<a:l.insert(0,i)cnt+=1else:l.append(i)return l #這樣可以得到一個成功左都更小右都更大的“進(jìn)步了的”數(shù)組#寫遞歸主體(基于上行過程中的合并操作)left=self.quickSort(alist[:cnt+1]) #這里必須要是cnt+1,之所以+1是因?yàn)橐欢ㄒ褏⒖紨?shù)據(jù)劃分到左邊去作為最右元素,不然它要是在右側(cè),就又會變成右半部分的參考元素,但是事實(shí)上已經(jīng)不需要了,我一開始只是覺得它重復(fù)作為參考元素沒有意義,但我仔細(xì)一想,重復(fù)作為參考元素會導(dǎo)致右半邊無法順利遞歸下去,大問題,所以必須得注意。right=self.quickSort(alist[cnt+1:])return left.extend(right)②雙指針(對撞指針)(不需要額外空間)
def quickSort(self,alist):#觸底條件(遞歸結(jié)束條件)if len(alist)<=1:return alist#寫出每一次順利二分的操作a=alist[0]lefthand=1righthand=len(alist)-1done=Falsewhile not done:while lefthand<=righthand and alist[lefthand]<=a:lefthand+=1while lefthand<=righthand and alist[righthand]>=a:righthand-=1if lefthand>righthand:done=Trueelse:temp=alist[lefthand]alist[lefthand]=alist[righthand]alist[righthand]=temptemp=alist[righthand]alist[righthand]=alist[0]alist[0]=tempreturn alist#寫遞歸主體self.quickSort(alist[:righthand])self.quickSort(alist[righthand+1:])#這邊要return嗎3.二分查找
3.1 二分查找
①自己寫的遞歸:
class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""#遞歸結(jié)束條件if len(nums)==0:return -1if len(nums)==1 and nums[0]!=target:return -1#內(nèi)部實(shí)現(xiàn)邏輯+遞歸主體mid=len(nums)//2if nums[mid]==target:return midelif nums[mid]>target:return self.search(nums[:mid],target)else:res=self.search(nums[mid+1:],target)if res==-1:return reselse:return mid+1+res②迭代(使用雙指針)
class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""if len(nums)==0:return -1left=0right=len(nums)-1while left<=right: #必須是帶=,不然只剩兩個并且是右邊那個的時候會漏掉mid=(left+right)/2if nums[mid]==target:return midelif nums[mid]>target:right=mid-1else:left=mid+1return -13.2類似的題:x的平方根
①迭代的二分查找(用到雙指針)
class Solution(object):def mySqrt(self, x):""":type x: int:rtype: int"""left=1right=xmid=(left+right)//2while left<=right:if mid**2==x:return midelif mid**2<x:left=mid+1else:right=mid-1return right? 因?yàn)樗皇橇斜?#xff0c;所以不能直接切片之類的操作,所以即使是遞歸也是需要用到雙指針的。但是我想了一下,好像因?yàn)檫@個返回值就是它的根,沒辦法用遞歸。
②牛頓迭代法
? 有時間再說吧。
3.3 可以深入理解二分的題:搜索旋轉(zhuǎn)排序數(shù)組
① 自己寫的:分步法(使用了遞歸)
? 整體思路大概是:1.找到兩個有序的分界處(使用普通的迭代法);2.最后一個元素是分界元素,可以通過比較最后一個元素和target的大小來判斷是在哪半個序列里; 3.在確定的序列中再進(jìn)行常規(guī)的二分查找。
? 整體將近50行,且在分界取切片和在其中二分查找的接口處容易出現(xiàn)很多忽略的邊界情況(也直接導(dǎo)致提交了好幾次都不對,調(diào)試了半個多小時才順利通過。)
class Solution(object):#常規(guī)的二分查找def s(self,nums,target):if len(nums)==0:return -1if len(nums)==1 and nums[0]!=target:return -1mid=len(nums)//2if target==nums[mid]:return midelif target<nums[mid]:return self.s(nums[:mid],target)else:r=self.s(nums[mid+1:],target)if r==-1:return relse:return mid+1+r#主體函數(shù)def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""if nums==[]:return -1if len(nums)==1:return 0 if nums[0]==target else -1cur=0l=len(nums)while cur<l-1:if nums[cur]<=nums[cur+1]:cur+=1else:breakif cur==l-1:return self.s(nums, target)cur+=1a=nums[-1]if target>a:return self.s(nums[:cur], target)else:#return cur+self.s(nums[cur:],target)res=self.s(nums[cur:],target)if res==-1:return reselse:return cur+res②官方題解:二分變體【迭代】
? 這個一方面可以理解為把那個尋找斷點(diǎn)的操作集成到了二分里面,不過這也證明了二分所要求的“有序”,其實(shí)也沒有那么死板;另一方面可以理解為二分的變種進(jìn)化,有一種遞歸的感覺,但是答案使用的是迭代,不一定非要有序,只要最后最底層的那個是有序的就行。
class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""left=0right=len(nums)-1while left<=right:mid=(left+right)//2if nums[mid]==target:return midif nums[left]<=nums[mid]:if nums[left]<=target<=nums[mid]:right=mid-1else:left=mid+1else:if nums[mid]<=target<=nums[right]:left=mid+1else:right=mid-1return -1③自己仿寫的:二分變體【遞歸】
class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""if not nums:return -1mid=len(nums)//2if nums[mid]==target:return midif len(nums)==1 and nums[0]!=target:return -1if nums[mid]>=nums[0]:if nums[0]<=target<nums[mid]:return self.search(nums[:mid],target)else:res=self.search(nums[mid+1:],target)if res==-1:return reselse:return mid+1+resif nums[mid]<=nums[-1]:if nums[mid]<target<=nums[-1]:res=self.search(nums[mid+1:],target)if res==-1:return reselse:return mid+1+res else:return self.search(nums[:mid],target)3.4 整理幾個關(guān)鍵點(diǎn)!!
當(dāng)?shù)鷷r,用的就是while left<=right,使用雙指針這樣循環(huán);當(dāng)遞歸時,就是以下面那個作為終止條件:
if len(nums)==0:return -1 if len(nums)==1 and nums[0]!=target:return -1用到遞歸時,如果要返回下標(biāo),涉及右半部分的時候要注意判斷并加上上一步的mid(即得到的是相對坐標(biāo),要加上參考系坐標(biāo)),可以使用如下方式:
res=self.search(nums[mid+1:],target) if res==-1:return res else:return mid+1+res總結(jié)
以上是生活随笔為你收集整理的【leetcode记录02】递归的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: datawhale组队学习笔记(2)链表
- 下一篇: 苹果发行 52.5 亿美元债券:规模超出