大家好,今天我們來看一下回溯演算法。
在開始之前,我們先來回顧一下貪心演算法。如果不熟悉的同學可以看這篇文章從哈夫曼編碼中我們學到了什麼?。
貪心演算法只能根據當前的狀態,選擇最優的走法,走向下一步,就和人的一生一樣,只能在岔路口選擇一條當前條件下最優的路走,過去就過去了,不能回退,只能一條路走到黑。而回溯演算法,可以有重來的機會,比如選擇了一條路,發現這條路不適合自己,然後回退到岔路口,重新來選擇。這就是回溯的思想(類似於可以穿越一樣)。
回溯演算法本質上就是列舉,我們列舉所有的解,然後去找到滿足期望的解。為了有規律地列舉所有可能的解,避免遺漏和重複,我們把問題求解的過程分為多個階段。每個階段,我們都會面對一個岔路口,我們先隨意選一條路走,當發現這條路走不通的時候(不符合期望的解),就回退到上一個岔路口,另選一種走法繼續走。
理論太過於抽象,我們來舉一個經典的例子-八皇后問題,來更深入的理解一下回溯的思想。首先我們來看一下題目:
我們有一個 8x8 的棋盤,希望往裡放 8 個棋子(皇后),每個棋子所在的行、列、對角線都不能有另一個棋子。如下圖所示:圖中圓圈代表皇后,顯然下圖的放置方法是不滿足條件的。
八皇后問題就是期望找到所有滿足這種要求的放置棋子的方式。我們可以這麼考慮,我們把這個問題劃分成8個階段,然後將8個棋子分別放到第一行、第二行...直到最後一行。在放置的過程中,我們不停地檢查當前放法,是否滿足要求。如果滿足,則跳到下一行繼續放置棋子;如果不滿足,那就再換一種放置的方法,然後再繼續嘗試。 我們來看一下程式碼是如何實現的。
class Cal8Queens:
result=[0 for i in range(8)]
count=0
def findResult(self,row):
if row==8:
#放置ok,輸出結果
self.printResult(self.result)
return
for col in range(8):
#考察放置是否滿足
if self.isResult(row,col):
self.result[row]=col
#繼續考察下一行
self.findResult(row+1)
def isResult(self,row,col):
#判斷row行column列放置是否合適
leftup=col-1
rightup=col+1
for i in range(row-1,-1,-1):
#考慮正上方是否有皇后
if self.result[i]==col:
return False
if leftup>=0:
#考慮左上角是否有皇后
if self.result[i]==leftup:
return False
if rightup<8:
# 考慮右上角是否有皇后
if self.result[i]==rightup:
return False
leftup=leftup-1
rightup=rightup+1
return True
def printResult(self,result):
self.count=self.count+1
for row in range(8):
for col in range(8):
if result[row]==col:
print("Q",end=" ")
else:
print("*",end=" ")
print()
print("===============")
print()
cal=Cal8Queens()
cal.findResult(0)
print(cal.count)
複製程式碼
儘管回溯演算法原理很好理解,當時卻可以解決很多問題。比如深度優先搜尋、0-1揹包問題等,如果要想完全掌握,還是需要多多練習。
今天的分享就到這裡,更多硬核知識,請關注公眾號“程式設計師學長”。