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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python 拓扑排序_拓扑排序(topsort)算法详解

發(fā)布時(shí)間:2025/3/19 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python 拓扑排序_拓扑排序(topsort)算法详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在圖論中,由某個(gè)集合上的偏序得到全序的策略就是拓補(bǔ)排序算法。拓?fù)渑判虺3霈F(xiàn)在涉及偏序關(guān)系的問題中,例如時(shí)序的先后、事物的依賴等。針對這些問題拓?fù)渑判蛲ǔD苡行У亟o出可行解。

為了便于理解,我們先來看一個(gè)實(shí)例,開源軟件常使用GNU make工具來管理項(xiàng)目的構(gòu)建,這里的“項(xiàng)目”是由若干個(gè)“對象”構(gòu)成的。Makefile文件則描述了這些“對象”的構(gòu)建規(guī)則,即給出一系列對象間的依賴關(guān)系。若對象A依賴于對象B,則說明對象B必須先于對象A構(gòu)建,否則構(gòu)建將無法進(jìn)行。make的任務(wù)就是合理安排各個(gè)對象構(gòu)建的先后順序,使得過程能順利地完成。

作為例子,一個(gè)Makefile文件的內(nèi)容如下:每行描述一個(gè)規(guī)則。例如第一行指明對象foo.o和bar.o必須先于target構(gòu)建。

target: foo.o bar.o

foo.o: foo.c foo.h

bar.o: bar.c bar.h

我們先對問題進(jìn)行數(shù)學(xué)轉(zhuǎn)化。離散數(shù)學(xué)為我們描述對象間的關(guān)系提供了有力工具——偏序。令X為所有要研究的對象的集合。集合X上的一個(gè)關(guān)系R是偏序,當(dāng)且僅當(dāng)R滿足自反性、對稱性、傳遞性。

定義如下關(guān)系:xRy:x必須先于y被構(gòu)建,即y依賴x,因?yàn)镽滿足偏序的性質(zhì),xRy也記為x?y。到此我們成功地對問題進(jìn)行了建模。接下來使用DAG來表示每個(gè)對象間的關(guān)系,圖的每一個(gè)頂點(diǎn)表示一個(gè)對象、有向線段表示關(guān)系?起點(diǎn)?終點(diǎn)。

(圖一)

如何合理布局各個(gè)對象的構(gòu)建順序,使得構(gòu)建過程可以順利地進(jìn)行下去呢?一種直觀的想法是:先選擇不被其它對象依賴的作為第1個(gè)對象;再考慮第2個(gè)對象,它除了已選的第1個(gè)對象外,不應(yīng)該被其它對象依賴;選擇第n個(gè)對象,它除了前面已選的第1~n-1對象外,不能再被其它對象依賴。按照這個(gè)規(guī)則依次選出對象,即可保證構(gòu)建過程順利結(jié)束。

可以認(rèn)為在DAG中,這種直觀想法是正確的。這種策略的結(jié)果用下圖描述,但是,這并不是真正的拓?fù)渑判?#xff1a;

(圖二)

問題在于,得到的圖并沒有反映排序結(jié)果,充其量不過把圖重新擺了一個(gè)形態(tài),而圖所描述的關(guān)系并沒有本質(zhì)的改變。如何解決這一問題?這就要引入全序。從圖中直觀地看出,只有部分對象之間具有偏序關(guān)系,作為反例,bar.h與bar.c之間無偏序關(guān)系,因此R不是集合X上的全序關(guān)系。試想在圖中,如果為每一對不能比較的對象,強(qiáng)制添加一個(gè)關(guān)系u?v(或v?u),使得集合X中每兩個(gè)對象都能建立關(guān)系,則R就成為了X上的全序關(guān)系,如圖三所示。

(圖三)

按照Hass圖的順序排列各個(gè)頂點(diǎn)得到圖三,我們發(fā)現(xiàn)從最底部頂點(diǎn)bar.c出發(fā),總有一條路徑能走完所有頂點(diǎn)并到達(dá)最頂部頂點(diǎn)target,另一個(gè)重要的觀察是,對于圖中任意一對頂點(diǎn)u和v,若邊∈Edges,則u在線性序列中出現(xiàn)在v之前,因此我們得到的結(jié)果拓?fù)溆行颉>€性排列所有頂點(diǎn),如下圖所示:

這個(gè)結(jié)果便是原問題的拓?fù)渑判颉R驗(yàn)樘砑雨P(guān)系u?v的方法不一定唯一,所以拓?fù)渑判虿灰欢ㄎㄒ弧5珶o論哪種情況,拓?fù)渑判蚨紳M足一個(gè)關(guān)鍵的性質(zhì):沒有一個(gè)節(jié)點(diǎn)指向它前面的節(jié)點(diǎn),形式化地描述:對于圖中的任意兩個(gè)結(jié)點(diǎn)u和v,若存在一條有向邊,則在拓?fù)渑判蛑衭一定出現(xiàn)在v前面。這條性質(zhì)描述了拓?fù)渑判虻谋举|(zhì),為我們編寫可行的算法提供了依據(jù)。

另外一些需要補(bǔ)充的定理是,有向圖拓?fù)渑判虼嬖诘某浞直匾獥l件是圖為DAG(有向無環(huán)圖),這個(gè)結(jié)論用于判斷問題是否有解,也可用于判斷一個(gè)有向圖是否有環(huán)。

算法的求解過程如下:首先統(tǒng)計(jì)所有頂點(diǎn)的入度。然后:

a.

尋找所有入度為0的頂點(diǎn),追加到結(jié)果序列末尾并將其從圖中移除,同時(shí)將其所有鄰接頂點(diǎn)的入度減一。

b.

重復(fù)a,直到所有頂點(diǎn)都從圖中移除。

算法結(jié)束時(shí),所得結(jié)果序列便是最終答案。

對于任意一個(gè)可能帶環(huán)的有向圖,在尋找入度為0的頂點(diǎn)時(shí),如果找不到,說明圖的拓?fù)渑判蚴遣淮嬖诘?#xff0c;即問題無解。

上述的“移除”是邏輯層面的概念,具體實(shí)現(xiàn)中,我們不需要真正地將頂點(diǎn)從圖中移除,因?yàn)槟炒蝍.中找到的入度為0的頂點(diǎn)只可能出現(xiàn)在上一次a.中入度被減一的頂點(diǎn)中。當(dāng)a找到入度為0的頂點(diǎn)時(shí),就會把它的鄰接頂點(diǎn)的入度減一,這時(shí)便可以順便統(tǒng)計(jì)入度減為0的頂點(diǎn),下次a直接從這些入度為0的頂點(diǎn)開始,無需再從整個(gè)圖中尋找入度為0的頂點(diǎn)。

最后通過一道UVa的題目來說明算法的具體實(shí)現(xiàn):

UVa10305(Ordering Tasks)

題目大意

給出一堆任務(wù),其中一個(gè)任務(wù)必須在它依賴的所有任務(wù)都完成后才能執(zhí)行。已知任務(wù)之間的關(guān)系,求可能的執(zhí)行順序。

分析

思路與make的例子一致。這里使用vector存儲鄰接表,數(shù)組deg_in維護(hù)每個(gè)頂點(diǎn)的入度,隊(duì)列que維護(hù)每趟中入度被減為0的頂點(diǎn)。

參考代碼

#include #include

#define N 100+2

using namespacestd;static vectorcon[N];static intdeg_in[N];int main(void) {

ios::sync_with_stdio(false);intn,m;while((cin >> n >> m) &&n) {for(int i=1;i<=n;++i) {

con[i].clear();

deg_in[i]= 0;

}for(int i=0; i

cin>> a >>j;

con[a].push_back(j);++deg_in[j];

}//

//找出第一個(gè)度為0的頂點(diǎn)// queueque;

vectorans;for(int i=1; i<=n; ++i) {if (!deg_in[i]) {

que.push(i);

}

}//

//求排序中其它n-1個(gè)頂點(diǎn)// while(!que.empty()) {int u =que.front();

que.pop();

ans.push_back(u);for(size_t i=0; i

que.push(t);

}

}

}for(size_t i=0; i

cout<< ans[i] << (i==ans.size()-1 ? "" : " ");

}

cout<

}return 0;

}

總結(jié)

以上是生活随笔為你收集整理的python 拓扑排序_拓扑排序(topsort)算法详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。