图综合练习--拓扑排序_拓扑排序
一個場景
在大學里,每當到了期末的時候,你一定會頭疼于選課給你帶來的困擾。其中一項就是先修課的問題,每次你高高興興地選了自己心儀的選修課時,卻發現自己不滿足先修課的要求,只好默默地退掉。到底怎樣安排我們的課才能保證不會有這種麻煩呢?這一次,我們就來討論一下這個問題。
假設大學里開設了這些課程:高等數學,線性代數,概率論,數據結構,機器學習和計算機視覺。它們存在這樣的先修關系:線性代數的先修課為高等數學;概率論的先修課是線性代數;機器學習的先修課為線性代數,概率論和數據結構;最后計算機視覺的先修課是機器學習。
下面我們需要把這些課程和關系做一個抽象化的處理,說白了就是能夠讓計算機讀懂這些關系。這里我們用圖來表示這些關系,把每門課程當做圖的節點,兩門課的先修關系抽象為圖的邊,并且是帶有方向的邊,從一個節點
指向另一個節點 表示 是 的先修課。要是我們把這些關系用圖表示出來就是下面這樣:這里,我們對上圖進行類似“排序”的操作,也就是怎樣安排這些課程使得一些課程不會因為先修課的原因而沒有被選上,我們把這種操作叫做拓撲排序(topological sorting)。
卡恩算法
下面我們來分析一下這個問題,它跟我們前面提到的減治有什么關系呢?假如我們要在某一個學期修計算機視覺這門課,我們首先要保證修了機器學習這門課,而要達到修機器學習這門課的條件,我們又要修三門前置課,這樣問題的規模在逐漸減小,直到這門課沒有任何先修條件,我們就可以直接修這門課。
卡恩算法為我們提供了具體的步驟。
- 步驟 1:在圖中找到沒有被其它節點所指向的節點,即入度(在圖論中,入度指的是所有進入該節點的節點個數)為 0 的節點。
- 步驟 2:刪除該節點以及對應的邊。
- 步驟 3:重復步驟 1 和步驟 2,直到圖中沒有入度為 0 的節點為止。
最后我們用代碼來實現一下:
首先構建節點 Vertex 類:
class Vertex(object):def __init__(self, subject):self.subject = subject # 科目然后寫出尋找入度為 0 的節點的函數:
def find_source(G):source = None # 初始化為 NoneV = G.keys(); E = G.values() # 分別獲取節點和邊的集合for v in V:for neighbour in E:for vertex in neighbour:if vertex is v:break # 如果 v 指向了其他節點就退出循環else:continue # 否則查看下一條邊break # 如果 v 指向了其他節點就退出循環 else: # 正常退出循環source = v break # 找到了入度為 0 的節點后退出最外層循環return source最后寫出我們的主函數,我們用pop方法從圖G中每次 pop 出入度為 0 的節點,但如果發現沒有入度為 0 的節點,就說明這樣的拓撲排序不存在,因此我們就提前終止程序。
def topo_main(G):order = [] # 存放排序后的節點while len(G) != 0:s = find_source(G) # 尋找入度為 0 的節點if s is None:return "不存在拓撲排序." # 退出函數order.append(s.subject)G.pop(s) # 刪除每次循環中入度為 0 的節點return order在主程序中,我們先構建一個圖G,然后執行topo_main函數,最后得到排序后的結果:
>>> print(topo_main(G)) >>> 排序結果為 ["高等數學", "線性代數", "概率論", "數據結構", "機器學習", "計算機視覺"]其實,除了卡恩算法之外,我們還可以用 DFS 的方法。由于它跟減治的關系不是很大,這里我們就不做討論了,感興趣的同學可以去網上搜一搜這方面的內容。
→ 本節全部代碼 ←
← 插入排序 | 算法與復雜度?zhuanlan.zhihu.com→ 二分查找與二叉樹 | 算法與復雜度?zhuanlan.zhihu.com總結
以上是生活随笔為你收集整理的图综合练习--拓扑排序_拓扑排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bk3432开发的应用实例_《Javaw
- 下一篇: 招商银行信用卡中心华泰证券暑期实习软开笔