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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

图形结构:遍历模型,分治法,动态规划,回溯法,BFS,DFS

發布時間:2024/9/15 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 图形结构:遍历模型,分治法,动态规划,回溯法,BFS,DFS 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

圖形結構,是樹形結構的擴展。

我們在回溯法里面了解到幾種結構:二叉樹,排列樹,完全n叉樹,這幾種解空間類型,都可以直接使用回溯法的框架解決。

二叉樹,排列樹,完全n叉樹,都可以看成x叉樹的變形,而圖形結構就是x叉樹。

在此之前,我們先明白一點:一顆二叉樹是什么,他是某一顆二叉樹的子樹,同樣的道理,一個圖是什么,他是某個圖的子圖。

一般的二叉樹問題,我們先處理當前結點,再處理左子樹和右子樹,對應一般的圖的問題,我們先處理當前頂點,再依次處理各個子圖。

在回溯法里,都是看成解空間的深入,對應這里的就是子問題的逐步變小,回溯法使用條件是:解空間滿足多米諾性質,也就是部分解不滿足最終解,部分解后面的解也就不滿足最終解,子問題的逐步縮小也是滿足多米諾性質的。

圖的寬度優先遍歷,和限界分支法類似,應該可以使用基于隊列方式或者優先級隊列的方式,因為圖是一般形式的x叉樹,每一層的叉不知道幾個,都是借助于標記來判斷是否完全遍歷的,注意標記visited的用法。

又思考了一下,為什么圖會使用標記visited,其本質是:一個圖分解成:當前頂點+相鄰頂點各個子圖,這種劃分是分治,但是劃分的子問題有重疊,子問題多且相互不獨立時,使用動態規劃編程時使用備忘模型,一般使用標記數組避免重復計算子問題。

# 圖的定義,鄰接表,使用字典的方式,用于演示,比較直觀g = {'A':{'B':1,'C':2},'B':{'A':1,'C':3,'D':4},'C':{'A':2,'B':3,'D':5,'E':6},'D':{'B':4,'C':5,'E':7,'F':8},'E':{'C':6,'D':7,'G':9},'F':{'D':8},'G':{'E':9}} # 圖的遍歷,寬度優先,也就是從起點開始,一層一層掃描,需要使用隊列數據結構 # 同時圖的遍歷,不同于樹的遍歷,樹的結構確定了,而圖點和點的相連情況無法提前確定 # 搜索時,需要標記已掃描的點,避免重復掃描def BFS(graph,start_vertex):# 初始化隊列,把起點放入隊列queue =[start_vertex,]# 我們還需要定義一個數組記錄已訪問的頂點,一般使用哈希表,每次查找時間復雜度O(1)visited = set()# 記錄遍歷的結果result = []while queue:# 彈出一個頂點current_vertex = queue.pop(0)# 假如該節點沒有訪問過,就訪問它,并標記已訪問if current_vertex not in visited:result.append(current_vertex)visited.add(current_vertex)# 下面就需要訪問這個結點的鄰接頂點for neighbour_vertex in graph[current_vertex].keys():# 把鄰接頂點放入隊列,放入之前可以判斷一下,是否已經訪問過 # if neighbour_vertex not in visited:queue.append(neighbour_vertex)return result# 這里面判斷結點是否訪問過,在彈出的時候判斷,可以改進一下,也就是在入隊的時候 # 就標記成已訪問的頂點,也就是重復的結點不會入棧 def BFS2(graph,start_vertex):queue = [start_vertex,]# 入棧就標記已訪問visited = set()visited.add(start_vertex)# 用于記錄訪問的結點順序result =[]while queue:current_vertex = queue.pop(0)result.append(current_vertex)for neighbour_vertex in graph[current_vertex].keys():if neighbour_vertex not in visited:queue.append(neighbour_vertex)visited.add(neighbour_vertex)return result

圖的深度優先遍歷:

# 這里面也需要注意標記的用法,我們在什么時候標記頂點已經被訪問了 # 第一次訪問頂點的時候,還是打印結點結束也就是最后一次訪問結點的時候 # 一般情況下我們習慣在在一個結點確定訪問完畢也就是最后訪問結點的時候,標記結點已訪問 # 實際操作的效率高的是,在我們第一次碰見頂點的時候就標記結點,因為第一次碰見他就代表 # 處理他已經開始了 # BFS在入隊列的時候,標記已訪問,這樣重復的結點就不會入隊列 # DFS在進入遞歸前,標記頂點已訪問,這樣重復結點就不會進入遞歸 # DFS的遞歸,把原問題看成:當前頂點+ 所有相鄰頂點為起點子圖的子問題 # 為什么不是當前頂點+ 一個相鄰頂點為起點子圖的子問題?想一下你要處理的是當前頂點+ # 剩余n-1個頂點構成的子圖,這個子圖可能是好幾個分離的圖。 def DFS(graph,start_vertex): def DFSRecursion(graph,start_vertex,visited): result.append(start_vertex) # 遍歷訪問子圖集,訪問前先判斷是否已訪問,未訪問標記已訪問,然后進入子圖處理for neighbour_vertex in graph[start_vertex].keys():if neighbour_vertex not in visited:visited.add(neighbour_vertex)DFSRecursion(graph,neighbour_vertex,visited)result =[] visited = set()# 在進入遞歸前,把頂點標記為已訪問visited.add(start_vertex) DFSRecursion(graph,start_vertex,visited)return result# 深度優先遍歷,明顯需要用到棧的數據結構 # 一個圖的處理,分為兩個大部分,當前頂點處理 + 所有子圖的處理, # 所以這個問題的結構是n叉樹, # 可以參考,回溯法處理(基于深度優先的回溯法,在那里使用迭代的方法比較少), # 或者參考二叉樹的處理,先序遍歷,先訪問頂點,再左子樹,后訪問右子樹,逆序放入棧中 # 那么對應圖的先序遍歷,先訪問頂點,在訪問各個子圖,只要把他們逆序放入棧中就行了 # 同理放入棧中前,先標記頂點已訪問 def DFS2(graph,start_vertex):visited = set()# 在入棧之前需要先看是否已標記,未標記標記之后放入棧中visited.add(start_vertex)stack =[start_vertex,]result = []while stack:current_vertex = stack.pop()# 處理當前結點result.append(current_vertex)# 處理子圖子問題for neighbour_vertex in graph[current_vertex].keys(): # 子問題入棧前需要判斷是否已標記,未標記標記之后放入棧中if neighbour_vertex not in visited:visited.add(neighbour_vertex)stack.append(neighbour_vertex)return result

測試結果:

print(BFS(g,'A')) print(BFS2(g,'A')) print(DFS(g,'A')) print(DFS2(g,'A'))runfile('D:/share/test/graph.py', wdir='D:/share/test') ['A', 'B', 'C', 'D', 'E', 'F', 'G'] ['A', 'B', 'C', 'D', 'E', 'F', 'G'] ['A', 'B', 'C', 'D', 'E', 'G', 'F'] ['A', 'C', 'E', 'G', 'D', 'F', 'B']

不要認為深度優先遍歷的遞歸方式和迭代方式結果不一樣,假如需要一樣,那迭代棧實現方式就需要逆序放入棧。

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的图形结构:遍历模型,分治法,动态规划,回溯法,BFS,DFS的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。