數(shù)據(jù)結(jié)構(gòu)與算法-拓?fù)渑判?/h1>

拓?fù)渑判蚪榻B

我們會把施工過程唱凯、生產(chǎn)流程、軟件開發(fā)么鹤、教學(xué)安排等都當(dāng)成一個項(xiàng)目工程來對待,所有的工程都可分為若干個“活動”的子工程味廊。例如下圖一張簡陋的電影制作流程圖蒸甜,現(xiàn)實(shí)中可能并不完全相同,但基本表達(dá)了一個工程和若干個活動的概念余佛。在這些活動之間柠新,通常會受到一定的條件約束,如其中某些活動必須在另一些活動完成之后才能開始辉巡。就像電影制作不可能在人員到位進(jìn)駐場地時恨憎,導(dǎo)演還沒有找到,也不可能在拍攝過程中,場地都沒有憔恳。這都會導(dǎo)致荒謬的結(jié)果瓤荔。因此這樣的工程圖,一定是無環(huán)的有向圖钥组。

在一個表示工程的有向圖中输硝,用頂點(diǎn)表示活動,用弧表示活動之間的優(yōu)先關(guān)系程梦,這樣的有向圖為頂點(diǎn)表示活動的網(wǎng)点把,我們稱為AOV網(wǎng)(ActivityOn Vertex Network)。AOV網(wǎng)中的弧表示活動之間存在的某種制約關(guān)系作烟。比如演職人員確定了愉粤,場地也聯(lián)系好了,才可以開始進(jìn)場拍攝拿撩。另外就是AOV網(wǎng)中不能存在回路衣厘。剛才已經(jīng)舉了例子,讓某個活動的開始要以自己完成作為先決條件压恒,顯然是不可以的影暴。

image-20200512094954667

設(shè)G=(V,E)是一個具有n個頂點(diǎn)的有向圖,V中的頂點(diǎn)序列v1型宙,v2,……伦吠,vn妆兑,滿足若從頂點(diǎn)vi到vj有一條路徑,則在頂點(diǎn)序列中頂點(diǎn)vi必在頂點(diǎn)vj之前毛仪。則我們稱這樣的頂點(diǎn)序列為一個拓?fù)湫蛄小?/p>

上圖這樣的AOV網(wǎng)的拓?fù)湫蛄胁恢挂粭l搁嗓。序列v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 是一條拓?fù)湫蛄校鴙0 v1 v4 v3 v2 v7 v6 v5 v8 v10 v9 v12 v11 v14 v13 v15 v16也是一條拓?fù)湫蛄小?/p>

所謂拓?fù)渑判蛳溲ィ鋵?shí)就是對一個有向圖構(gòu)造拓?fù)湫蛄械倪^程腺逛。構(gòu)造時會有兩個結(jié)果,如果此網(wǎng)的全部頂點(diǎn)都被輸出衡怀,則說明它是不存在環(huán)(回路)的AOV網(wǎng)棍矛;如果輸出頂點(diǎn)數(shù)少了,哪怕是少了一個抛杨,也說明這個網(wǎng)存在環(huán)(回路)够委,不是AOV網(wǎng)。

拓?fù)渑判蛩惴?/h1>

對AOV網(wǎng)進(jìn)行拓?fù)渑判虻幕舅悸肥牵簭腁OV網(wǎng)中選擇一個入度為0的頂點(diǎn)輸出怖现,然后刪去此頂點(diǎn)茁帽,并刪除以此頂點(diǎn)為尾的弧,繼續(xù)重復(fù)此步驟,直到輸出全部頂點(diǎn)或者AOV網(wǎng)中不存在入度為0的頂點(diǎn)為止脐雪。

首先我們需要確定一下這個圖需要使用的數(shù)據(jù)結(jié)構(gòu)厌小。前面求最小生成樹和最短路徑時,我們用的都是鄰接矩陣战秋,但由于拓?fù)渑判虻倪^程中璧亚,需要刪除頂點(diǎn),顯然用鄰接表會更加方便脂信。因此我們需要為AOV網(wǎng)建立一個鄰接表癣蟋。考慮到算法過程中始終要查找入度為0的頂點(diǎn)狰闪,我們在原來頂點(diǎn)表結(jié)點(diǎn)結(jié)構(gòu)中疯搅,增加一個入度域in,結(jié)構(gòu)如下表所示埋泵,其中in就是入度的數(shù)字幔欧。

in data first edge

因此對于下圖的第一幅圖AOV網(wǎng),我們可以得到如第二幅圖的鄰接表數(shù)據(jù)結(jié)構(gòu)丽声。

image-20200512102758454

在拓?fù)渑判蛩惴ㄖ薪刚幔婕暗慕Y(jié)構(gòu)代碼如下。

/* 邊表結(jié)點(diǎn) */
typedef struct EdgeNode          
{
    /* 鄰接點(diǎn)域雁社,存儲該頂點(diǎn)對應(yīng)的下標(biāo) */
    int adjvex;                  
    /* 用于存儲權(quán)值浴井,對于非網(wǎng)圖可以不需要 */
    int weight;                  
    /* 鏈域,指向下一個鄰接點(diǎn) */
    struct EdgeNode *next;       
} EdgeNode;
/* 頂點(diǎn)表結(jié)點(diǎn) */
typedef struct VertexNode        
{
    /* 頂點(diǎn)入度*/
    int in;                      
    /* 頂點(diǎn)域霉撵,存儲頂點(diǎn)信息 */
    int data;                    
    /* 邊表頭指針 */
    EdgeNode *firstedge;         
} VertexNode, AdjList[MAXVEX];
typedef struct
{
    AdjList adjList;
      /* 圖中當(dāng)前頂點(diǎn)數(shù)和邊數(shù) */
    int numVertexes,numEdges;    
} graphAdjList, *GraphAdjList;

在算法中磺浙,我還需要輔助的數(shù)據(jù)結(jié)構(gòu)—棧,用來存儲處理過程中入度為0的頂點(diǎn)徒坡,目的是為了避免每個查找時都要去遍歷頂點(diǎn)表找有沒有入度為0的頂點(diǎn)撕氧。

/* 拓?fù)渑判颍鬐L無回路崭参,則輸出拓?fù)渑判蛐蛄胁⒎祷豋K呵曹,若有回路返回ERROR */
Status TopologicalSort(GraphAdjList GL)
{
    EdgeNode *e;
    int i, k, gettop;
    /* 用于棧指針下標(biāo) */
    int top = 0;                                       
    /* 用于統(tǒng)計(jì)輸出頂點(diǎn)的個數(shù) */
    int count = 0;                                     
    /* 建棧存儲入度為0的頂點(diǎn) */
    int *stack;                                        
    stack = (int *)malloc(GL->numVertexes * sizeof(int));
    for (i = 0; i < GL->numVertexes; i++)
       if (GL->adjList[i].in == 0)
           /* 將入度為0的頂點(diǎn)入棧 */
           stack[++top] = i;                          
    while (top != 0)
    {
        /* 出棧 */
        gettop = stack[top--];                         
        /* 打印此頂點(diǎn) */
        printf("%d -> ", GL->adjList[gettop].data);    
        /* 統(tǒng)計(jì)輸出頂點(diǎn)數(shù) */
        count++;                                       
        /* 對此頂點(diǎn)弧表遍歷 */
        for (e = GL->adjList[gettop].firstedge; e; e = e->next)
        {                                              
            k = e->adjvex;
            /* 將k號頂點(diǎn)鄰接點(diǎn)的入度減1 */
            if (!(--GL->adjList[k].in))                
                /*若為0則入棧款咖,以便于下次循環(huán)輸出 */
                stack[++top] = k;                      
        }
    }
    /*如果count小于頂點(diǎn)數(shù)何暮,說明存在環(huán) */
    if (count < GL->numVertexes)                       
        return ERROR;
    else
        return OK;
}

1.程序開始運(yùn)行,第3~7行都是變量的定義铐殃,其中stack是一個棧海洼,用來存儲整型的數(shù)字。

2.第8~10行富腊,作了一個循環(huán)判斷坏逢,把入度為0的頂點(diǎn)下標(biāo)都入棧,從下圖的右圖鄰接表可知,此時stack應(yīng)該為:{0,1,3}是整,即v0肖揣、v1、v3的頂點(diǎn)入度為0浮入,如下圖所示龙优。

image-20200512104919386

3.第12~23行,while循環(huán)事秀,當(dāng)棧中有數(shù)據(jù)元素時彤断,始終循環(huán)。

4.第14~16行易迹,v3出棧得到gettop=3宰衙。并打印此頂點(diǎn),然后count加1睹欲。

5.第17~22行供炼,循環(huán)其實(shí)是對v3頂點(diǎn)對應(yīng)的弧鏈表進(jìn)行遍歷,即下圖中的灰色部分窘疮,找到v3連接的兩個頂點(diǎn)v2和v13劲蜻,并將它們的入度減少一位,此時v2和v13的in值都為1考余。它的目的是為了將v3頂點(diǎn)上的弧刪除先嬉。

image-20200512105147056

6.再次循環(huán),第12~23行楚堤。此時處理的是頂點(diǎn)v1疫蔓。經(jīng)過出棧、打印身冬、count=2后衅胀,我們對v1到v2、v4酥筝、v8的弧進(jìn)行了遍歷滚躯。并同樣減少了它們的入度數(shù),此時v2入度為0嘿歌,于是由第20~21行知掸掏,v2入棧,如下圖所示宙帝。試想丧凤,如果沒有在頂點(diǎn)表中加入in這個入度數(shù)據(jù)域,20行的判斷就必須要是循環(huán)步脓,這顯然是要消耗時間的愿待,我們利用空間換取了時間浩螺。

image-20200512105411646

7.接下來,就是同樣的處理方式了仍侥。下圖展示了v2 v6 v0 v4 v5 v8的打印刪除過程要出,后面還剩幾個頂點(diǎn)都類似,就不圖示了农渊。

image-20200512105457837

8.最終拓?fù)渑判虼蛴〗Y(jié)果為3->1->2->6->0->4->5->8->7->12->9->10->13->11厨幻。當(dāng)然這結(jié)果并不是唯一的一種拓?fù)渑判蚍桨浮?/p>

分析整個算法,對一個具有n個頂點(diǎn)e條弧的AOV網(wǎng)來說腿时,第8~10行掃描頂點(diǎn)表况脆,將入度為0的頂點(diǎn)入棧的時間復(fù)雜為O(n),而之后的while循環(huán)中批糟,每個頂點(diǎn)進(jìn)一次棧格了,出一次棧,入度減1的操作共執(zhí)行了e次徽鼎,所以整個算法的時間復(fù)雜度為O(n+e)盛末。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末,一起剝皮案震驚了整個濱河市否淤,隨后出現(xiàn)的幾起案子悄但,更是在濱河造成了極大的恐慌,老刑警劉巖石抡,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檐嚣,死亡現(xiàn)場離奇詭異,居然都是意外死亡啰扛,警方通過查閱死者的電腦和手機(jī)嚎京,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隐解,“玉大人鞍帝,你說我怎么就攤上這事∩访#” “怎么了帕涌?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長续徽。 經(jīng)常有香客問我蚓曼,道長,這世上最難降的妖魔是什么炸宵? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任辟躏,我火速辦了婚禮谷扣,結(jié)果婚禮上土全,老公的妹妹穿的比我還像新娘捎琐。我一直安慰自己,他們只是感情好裹匙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布瑞凑。 她就那樣靜靜地躺著,像睡著了一般概页。 火紅的嫁衣襯著肌膚如雪籽御。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天惰匙,我揣著相機(jī)與錄音技掏,去河邊找鬼。 笑死项鬼,一個胖子當(dāng)著我的面吹牛哑梳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绘盟,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼鸠真,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了龄毡?” 一聲冷哼從身側(cè)響起吠卷,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沦零,沒想到半個月后祭隔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡路操,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年序攘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寻拂。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡程奠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出祭钉,到底是詐尸還是另有隱情瞄沙,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布慌核,位于F島的核電站距境,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏垮卓。R本人自食惡果不足惜垫桂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粟按。 院中可真熱鬧诬滩,春花似錦霹粥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至空镜,卻和暖如春浩淘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吴攒。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工张抄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人洼怔。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓欣鳖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親茴厉。 傳聞我的和親對象是個殘疾皇子泽台,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355