LeetCode學習 分治演算法思想

2021-10-23 12:31:44 字數 4182 閱讀 5027

之前我們說了遞迴的重點就是乙個終止條件(也叫邊界條件);此處的邊界條件就是f(n)中的n=1;當n=1時就表示遞迴進入了最低成,下一步就是要往上返回了。

# 算 n 的階乘(假設n不為0)

deff

(n):

if n==1:

return

1else

:return n*f(n-1)

print

(f(6))

#720

再來說這段簡單的**,由兩部分組成。乙個是終止條件n==1;當往深了走走到n=1了,開始往外返回計算的結果了;第二部分是 呼叫函式本身的部分了,就是處理問題的邏輯的方式了,階乘,大家都知道一路乘下去直到1。一般的遞迴最難的部分就是這個邏輯的編寫了,相當於什麼呢?相當於,你要找到乙個通用的辦法,來結合乙個數量級很大的運算;得多見識一些遞迴才好提高;

想想思路;斐波那契數是前面連續兩項相加得來的。那麼遞迴邏輯部分就可以大致寫成f(n) = f(n-2)+f(n-1);這個就是核心**,是不是看起來很簡單,那麼還差乙個終止條件是不是?觀察斐波那契數列的前幾個資料 f(1)=1 f(2)=1 f(3)=2前兩項是固定的都是1,那麼簡單了,遞迴的終止條件就是往前推到第2項或者第1項的時候就返回1,即,終止條件是:n<=2時返回第一或者第二項的值1;

def

f(n)

:if n<=2:

return

1else

:return f(n-2)

+f(n-1)

print

(f(20))

# 6765

def divide_conquer

(problem, paraml, param2,..

.): # 往裡走的終止條件

if problem is none:

print_result

return

# 準備資料

data=

prepare_data

(problem)

# 將大問題拆分為小問題

subproblems=

split_problem

(problem, data)

# 處理小問題,得到子結果

subresult1=self.

divide_conquer

(subproblems[0]

,p1,

..…)

subresult2=self.

divide_conquer

(subproblems[1]

,p1,..

.)subresult3=self.

divide_conquer

(subproblems[2]

,p1,

.…) # 對子結果進行合併 得到最終結果

result=

process_result

(subresult1, subresult2, subresult3,..

.)

在不用遞迴思路的情況下,我們第一思路就是做乙個n次的迴圈,每次迴圈都x乘上一次被複製的結果;

用分治思想來處理(從中間拆分成兩部分:偶數次冪對半分,奇數次冪單獨乘乙個x後再分(n-1)/ 2 ):

1,終止條件:思路是n次方分成兩個[n/2]次方相乘,最後n=0了表示不在拆分了;

2,準備資料,處理小問題:技術次冪先乘x再二分遞迴。偶數次冪直接二分遞迴;

def

f(x, n)

:# 【確定不斷切分的終止條件】

if n ==0:

return

1# 【準備資料,並將大問題拆分為小的問題】

# 奇數次冪先乘x,後剩下偶數次冪再對半遞迴

if n %2==

1:# 【處理小問題,得到子結果】

p = x * f(x, n -1)

return p

return f(x * x, n /2)

print

(f(3,5

))# 243

首先來說說快排的思路:每次都選擇乙個標誌位,比標誌小的放左邊,比標誌大的放右邊。至於這個標誌呢,就選定每邊的第乙個數字。好,選擇思路有了,怎麼實現,如果不考慮空間複雜度,我們的實現方式是新增新陣列,每次拆分排序後,生成兩個臨時陣列(乙個放小於標誌位的數,乙個放大於標識位的數),下一層遞迴再生成兩個新陣列,這個實現起來豈不是增加極大的記憶體浪費麼?有沒有辦法再不增加空間的情況下實現呢

在原先的陣列的結構彙總進行移動數字。如何實現呢?通過用乙個變數臨時存放標識位,那麼,標識為所在的陣列中的位置不久空出來了麼?只要陣列中有空位,便可實現數字交換了呀;具體實現是:首位交替掃瞄

什麼是首尾交替掃瞄呢?引用指標概念,left,right指標最開始分別指向陣列的首尾,取第乙個數字為標誌位,那就是nums[left];那麼left指向的位置空出來了,此時拿rigt位置的數跟標誌位比較,小,就把right位置的數放到left的位置上,left++(left原來的數字已經被存到標識位變數pivot中了),若比標誌位大,直接用right–比較(為什麼呢?因為大的在右邊啊,都最後乙個了還不夠右嗎?)。交換後,right位置是不是空的呀?這個時候就比較left的位置啦(為啥是從left++開始,left被right覆蓋後,left得++指向左數第二個元素了),繼續重複上面步驟。**如下:

# --

----

----

----

----

----

----

-快速排序--

----

-------

def quick_sort

(nums: list, left:

int, right:

int)

-> none:

#1,終止條件

if left < right:

i = left

j = right

# 取第乙個元素為標誌

pivot = nums[left]

while i != j:

# 交替掃瞄和交換

# 從右往左找到第乙個比標誌位小的元素,交換位置

while j > i and nums[j]

> pivot:

j -=

1if j > i:

# 如果找到了,進行元素交換

nums[i]

= nums[j]

i +=

1 # 從左往右找到第乙個比標誌位大的元素,交換位置

while i < j and nums[i]

< pivot:

i +=

1if i < j:

nums[j]

= nums[i]

j -=

1 # 至此完成一趟快速排序,標誌位的位置已經確定好了,就在i位置上(i和j)值相等

nums[i]

= pivot

# 以i為標誌進行子串行元素交換

quick_sort

(nums, left, i-1)

quick_sort

(nums, i+

1, right)

# 測試**

import random

data =

[random.

randint(-

100,

100)

for _ in range(10

)]print

(data)

quick_sort

(data,0,

len(data)-1

)print

(data)

# [23,-

77,5,

26,-67

,-41,

-19,-

89,84,

-56]# [-89

,-77,

-67,-

56,-41

,-19,

5,23,

26,84]

Leetcode 分治演算法

題目描述 在未排序的陣列中找到第 k 個最大的元素。請注意,你需要找的是陣列排序後的第 k 個最大的元素,而不是第 k 個不同的元素。示例 1 輸入 3,2,1,5,6,4 和 k 2 輸出 5 解法 堆 思路 建立乙個大頂堆,並保持堆的大小小於等於k。堆內的排序從堆頂遞增,最後的堆頂就是所求。時間...

分治演算法的學習筆記

分治演算法應用一 漢諾塔問題 問題描述 漢諾塔問題 於乙個古老的傳說,世界剛被建立的時候,有一座鑽石寶塔,上面有64個金碟,所有碟子按照從大到小的順序從塔底堆到塔頂,從世界創世開始,牧師們一直在努力將塔a的碟子借助b移到c上,每次只能移動乙個,而且不能讓小的放在大的下面 原理 對漢諾塔問題的求解簡化...

組隊學習 分治演算法 打卡

將原本複雜的問題,拆分成相同解決方法的簡單的小問題,然後將小問題的答案歸併成初始問題的解。採用遞迴的方式,將問題層層分解,直到子問題滿足設定好的終止條件結束遞迴求解子問題的解,然後將解歸併。示意圖如下 示例1 輸入 3,2,3 輸出 3 示例2 輸入 2,2,1,1,1,2,2 輸出 2 首先想到的...