深度優先搜索(一)

深度優先搜索(DFS)是一種基本的圖算法,主要針對圖和樹,英文縮寫:DFS。

深度優先搜索使用的策略:從一個頂點V0開始,沿着一條路一直走到底,如果發現不能到達目標解,那就返回到上一個節點,然後從另一條路開始走到底。

理解深度優先搜索的關鍵在於:解決“當下該如何做”,同時“下一步如何做”則和“當下該如何做”是一樣的。

通常的方法:把每一種可能都去嘗試一遍(一般使用for循環來遍歷),當前這一步解決後便進入下一步。下一步的解決方法和當前這步的解決方法是完全一樣的。

  • 在訪問其中一個頂點時,將它標記爲已訪問
  • 遞歸地訪問它的所有沒有被標記過的鄰居頂點。

【深度優先搜索算法要點】
⑴定義狀態。
即如何描述問題求解過程中每一步的狀況。在n皇后問題中,將行l位置a[l]作爲狀態。如果擴展結點時參與運算的變量有多個,爲了精簡程序,增加可讀性,我們一般將參與子結點擴展運算的變量組合成當前狀態列入值參,以便回溯時能恢復遞歸前的狀態,重新計算下一條路徑。
⑵邊界條件。
即在什麼情況下程序不再遞歸下去。在n皇后問題中,將l=n+1(產生一種成功擺法)作爲邊界條件。如果是求滿足某個特定條件的一條最佳路徑,則當前狀態到達邊界時並不一定意味着此時就是最佳目標狀態。因此還須增加判別最優目標狀態的條件。
⑶搜索範圍
在當前狀態不滿足條件的情況下,應如何設計搜索的範圍。換句話說,如何設定for語句中循環變量的初值和終值;在n皇后問題中,l行的列位置i作爲搜索範圍,即l<=i<=n。
⑷約束條件和最優要求。
所謂約束條件是指,當前擴展出一個子結點後應滿足什麼條件方可繼續遞歸下去;是求滿足某個特定條件的一條最佳路徑,那麼在擴展出某個子狀態後是否滿足最優性要求。在n皇后問題中,將(l,i)置放皇后不產生攻擊(att=false)作爲約束條件。
⑸參與遞歸運算的參數。
將參與遞歸運算的參數設爲遞歸子程序的值參或局部變量。若這些參數的存儲量大(例如數組)且初始值需由主程序傳入,爲避免內存溢出,則必須將其設爲全局變量,且回溯前需恢復其遞歸前的值,在n皇后問題中,將皇后的行位置l和列位置i作爲參與遞歸運算的參數。

【走迷宮】

深度優先搜索的重要例子——走迷宮。

用迷宮代替圖,通道代替邊,路口代替定點,那麼我們就可以將迷宮看成是一個圖。

要探索迷宮中的所有通道,我需要這樣做:(和DFS思想是一樣的)

  • 選擇一條沒有標記過的通道,在走過的路上鋪一條繩子
  • 標記所有第一次路過的路口和通道
  • 當遇到一個標記過的路口時回退到上一個路口
  • 當回退到的路口已經沒有可走的通道時繼續回退

圖例:

【題目】

迷宮由n行m列的單元格組成,每個單元格要麼是空地,要麼是障礙物,需要找到一條從起點通往終點的最短路徑。

注意:障礙物不能走,不能走到迷宮外面。

起點

 

障礙

 

 

 

 

 

 

 

障礙

 

 

障礙

終點

 

 

 

 

障礙

起點爲(1,1),只能往下走或往右走,我們一個一個進行嘗試。

  1.  先往右走,直到走不通的時候再往回回退,然後再去嘗試另外一個方向。
  2. 起點(1,1)一步之內可以到達的點爲(1,2)和(2,1),先往右走,到達(1,2)。
  3. 到達(1,2)後來判斷又能到達哪些點?只有(2,2),然後繼續往下走,直到無路可走或到達終點爲止。

下圖是一種可行的搜索路徑:

1

2

障礙

 

 

3

4

5

 

 

障礙

6

 

障礙

終點8

7

 

 

 

障礙

【DFS】

DFS函數的功能是解決當前應該怎麼辦。

在這道題目時,這一步需要解決的是:檢查是否已經到了終點,如果沒有到達終點則找出下一步可以走的地方。

因此,這裏的DFS()需要維護3個參數,當前點的x座標,y座標,已經走過的步數step。如下

def dfs(x,y,step):
    {
        return 0
    }

檢查是否已經到達終點,只需要判斷當前座標和終點座標是否相等就可以了,如果相等則表明已經到達終點的位置。

def dfs(x,y,step):
{
    #判斷是否到達終點位置
    if x == p and y == q:
        #更新最小值
        if step < min
            min = step
        return
    return 0
}

如果沒有到達終點,則找出下一步可以走的地方。但是有四個方向可以走,我先定義一個方向數組

next_step = [[0,1],  #向右走
            [1,0],  #向下走
            [0,-1], #向左走
            [-1,0]  #向上走
            ]

通過這個方向數組,使用循環就可以得到下一步的座標。

for i in range(len(next_step)):
        next_x = start_x + next_step[i][0]
        next_y = start_y + next_step[i][1]

接下來,要對下一個點進行判斷,包括是否越界,是否爲障礙物,是否這個點已經在路徑中(使用book[next_x][next_y]來記錄當前點是否已經在路徑中)

如果這個點符合所有的要求,就對這個點進行下一步,dfs(next_x,next_y,step+1).

def dfs(start_x,start_y,end_x,end_y,migong_array,step):
    '''
    :param start_x: 起始橫座標
    :param start_y: 起始縱座標
    :param end_x: 終點橫座標
    :param end_y: 終點縱座標
    :param migong_array: 迷宮的數組
    :return:
    '''
    next_step = [[0,1],  #向右走
            [1,0],  #向下走
            [0,-1], #向左走
            [-1,0]  #向上走
            ]
    if (start_x == end_x and start_y == end_y):
        global MIN
        if(step < MIN):
            MIN = step
        return

    for i in range(len(next_step)):
        next_x = start_x + next_step[i][0]
        next_y = start_y + next_step[i][1]
        # 判斷是否越界
        if(next_x < 0 or next_y < 0 or next_x > len(migong_array) or next_y > len(migong_array[0])):
            continue
        # 判斷是否爲障礙物和已經在路中
        if(a[next_x][next_y] == 0 and book[next_x][next_y] == 0):
            book[next_x][next_y] = 1 #標記這個點已經被走過
            dfs(next_x,next_y,end_x,end_y,migong_array,step+1)#嘗試下一個點
            book[next_x][next_y] = 0#嘗試結束,取消這個點的標記
    return

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章