生活随笔
收集整理的這篇文章主要介紹了
活动网络——用顶点表示活动的网络AOV和拓扑
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
活動網絡
7.6.1 用頂點表示活動的網絡AOV
問題的提出
學生選修課程問題
頂點,表示課程
有向弧,表示先決條件,若課程i是課程j的先決條件,則圖中有弧<i,j>
學生應按怎樣的順序學習這些課程,才能無矛盾、順利地完成學業——拓撲排序
相關定義
有向無環圖(directed acycline graph),簡稱DAG圖
第二個圖沒有環,是DAG,第三個圖有環,只能成為有向圖頂點表示活動(activity on vertices AOV)的網絡
AOV是用以表示一個工程的有向圖
圖中的頂點表示一項子工程,即活動
弧表示兩活動之間的先后次序
以計算機專業課程學習工程為例:
拓撲排序(Topological Sort):把AOV網絡中各頂點按照它們相互之間的優先關系排列成一個線性序列的過程。
檢測AOV網中是否存在環方法:對有向圖構造其頂點的拓撲有序序列,若網中所有頂點都在它的拓撲有序序列中,則該AOV網必定不存在環。
(存在換的話,意味著某項活動的開始要以自己的完成作為先決條件,這顯然是矛盾的)因此,對給定的AOV網絡,必須先判斷它是否存在有向環。
拓撲排序的方法:
(1)在有向圖中選一個沒有前驅的頂點且輸出之
(2)從圖中刪除該頂點和所有以它為尾的弧(3)重復上述兩步,直至全部頂點均已輸出;或者當圖中不存在無前驅的頂點為止。
注意:如果頂點有多個直接后繼,排序結果通常不唯一。即,拓撲排序的結果唯一的情況是:每個頂點有唯一的前驅后繼關系。
例如在下圖中,存在兩個拓撲有序序列,則說明拓撲排序不唯一。
C1,C2,C3,C4,C5,C6,C8,C9,C7
C1,C8,C9,C2,C5,C3,C7,C4,C6
時間復雜度O(n+e)
拓撲排序算法
存儲結構
AOV網絡:鄰接表
輔助變量:頂點入度數組InDegree[ ],記錄各個頂點的入度
算法步驟
(1)建立入度為零的頂點棧;
(2)當入度為零的頂點棧為空時算法轉步驟(6),否則繼續步驟(3);
(3)入度為零的頂點棧中棧頂元素v出棧,并輸出頂點v;
(4)從AOV網絡中刪去頂點v和所有從頂點v發出的弧<v , j >,并將頂點j的入度減一;
(5)如果頂點 j 入度減至0,則將該頂點進入入度為零的頂點棧;轉步驟(2);
(6)如果輸出頂點個數少于AOV網絡的頂點個數,則輸出網絡中存在有向環的信息;算法結束。
算法實現
邊節點類
template<class arcType> class ArcNode
{
public:int vex
;ArcNode
<arcType
> *nextarc
;ArcNode(){nextarc
=NULL;}ArcNode(int v
,ArcNode
<arcType
> *next
=NULL){vex
=v
;nextarc
=next
;}
};
頂點節點類
#include "ArcNode.h"
template<class vertexType,class ArcType> class VertexNode
{
public:vertexType vertex
;ArcNode
<ArcType
> *arcs
;VertexNode(){arcs
=NULL;}VertexNode(vertexType v
){vertex
=v
;arcs
=NULL;}
};
拓樸排序特殊鄰接表(刪除一些不必操作)實現
#define MaxVertexes 20
#include <iostream>
#include "VertexNode.h"
#include "ArcNode.h"using namespace std
;template<class vertexType,class arcType> class VertexNode;
template<class arcType> class ArcNode;class Graph
{friend class VertexNode<int,int>;friend class ArcNode<int>;
private:VertexNode
<int,int> *VertexesTable
;int *InDegree
;int CurrentNumVertexes
;int CurrentNumArcs
;int GetVertexPos(const int &v
);void Clear();public:Graph(int v
[],int num
=MaxVertexes
);~Graph();void InsertVertex(int v
);void InsertArc(int head
,int tail
);void Show()const;void TopologicalOrder();};
int Graph
::GetVertexPos(const int &v
)
{int i
;for(i
=0; i
<CurrentNumVertexes
&& VertexesTable
[i
].vertex
!=v
; i
++);if(i
>=CurrentNumVertexes
){cout
<<"沒有頂點的值為 "<<v
<<" ,返回-1"<<endl
;i
=-1;}elsereturn i
;
}
void Graph
::InsertVertex(int v
)
{int i
;for(i
=0; i
<MaxVertexes
; i
++)if(VertexesTable
[i
].vertex
==-1){VertexesTable
[i
].vertex
=v
;InDegree
[i
]=0;CurrentNumVertexes
++;break;}if(i
>=MaxVertexes
){cout
<<"頂點已滿。"<<endl
;}return;
}
void Graph
::InsertArc(int head
,int tail
)
{if(VertexesTable
[head
].arcs
==NULL){VertexesTable
[head
].arcs
=new ArcNode
<int>(tail
);InDegree
[tail
]++;return;}ArcNode
<int> *p
=VertexesTable
[head
].arcs
,*q
=NULL;while(p
!=NULL){if(p
->vex
==tail
){cout
<<"該頂點已在表中 "<<endl
;return;}else{q
=p
;p
=p
->nextarc
;}}q
->nextarc
=new ArcNode
<int>(tail
);CurrentNumArcs
++;InDegree
[tail
]++;
}
Graph
::Graph(int v
[],int num
):CurrentNumVertexes(0),CurrentNumArcs(0)
{int i
;VertexesTable
=new VertexNode
<int,int>[MaxVertexes
];InDegree
=new int[MaxVertexes
];for(i
=0; i
<MaxVertexes
; i
++) {VertexesTable
[i
].vertex
=-1;InDegree
[i
]=0;}for(i
=0; i
<num
; i
++) InsertVertex(v
[i
]);
}
void Graph
::Clear()
{int i
;ArcNode
<int> *p
=NULL,*q
=NULL;for(i
=0; i
<CurrentNumVertexes
; i
++){p
=VertexesTable
[i
].arcs
;while(p
!=NULL){q
=p
->nextarc
;delete p
;p
=q
;}p
=NULL;q
=NULL;VertexesTable
[i
].arcs
=NULL;InDegree
[i
]=0;}CurrentNumArcs
=0;CurrentNumVertexes
=0;
}
Graph
::~Graph()
{Clear();delete [] InDegree
;delete [] VertexesTable
;InDegree
=NULL;VertexesTable
=NULL;
}
void Graph
::Show()const
{int i
;ArcNode
<int> *p
=NULL;for(i
=0; i
<CurrentNumVertexes
; i
++){cout
<<"第"<<i
<<"個頂點("<<VertexesTable
[i
].vertex
<<"),有邊:";p
=VertexesTable
[i
].arcs
;while(p
!=NULL){cout
<<p
->vex
<<" -- ";p
=p
->nextarc
;}cout
<<"# ,入度為"<<InDegree
[i
]<<endl
;}
}
拓撲算法
void Graph
::TopologicalOrder()
{int i
,j
,count
=0,top
=-1;ArcNode
<int> *p
=NULL;for(i
=0; i
<CurrentNumVertexes
; i
++){if(InDegree
[i
]==0){InDegree
[i
]=top
;top
=i
;}}while(top
!=-1){i
=top
;top
=InDegree
[i
];cout
<<VertexesTable
[i
].vertex
<<" -- ";count
++;for(p
=VertexesTable
[i
].arcs
; p
!=NULL; p
=p
->nextarc
){j
=p
->vex
;InDegree
[j
]--;if(InDegree
[j
]==0){InDegree
[j
]=top
;top
=j
;}}}cout
<<"#"<<endl
;if(count
<CurrentNumVertexes
)cout
<<"圖中有回路"<<endl
;
}
測試結果:
效率分析
設AOV網絡中有n個頂點和e條邊,算法的第一個 for循環是搜索入度為零的頂點建立鏈棧,其所需要的時間是O(n)。
在拓撲排序的過程中,如果網絡中沒有回路,則網絡中所有頂點都需要進一次棧,出一次棧,所需要的時向也是O(n)。頂點入度減1的運算共執行了e次。
所以拓撲排序算法的時間復雜度為O(n+e)。
思考:
如何記錄入度為0的頂點?采用什么結構/輔助手段比較好?
【方法一】直接使用一個數組作為靜態棧,棧的使用方式如下:
(1)初始化棧,設置鏈初始值top為-1(表示空棧);
(2)將入度為0的元素的進棧,即InDegree[v]=top;top=v;(當前節點入度值來保存前一個結點,用top指向最后進入的結點)
(3)頂點入棧的同時“刪除”前一個的出邊(度數減一)。如果度數為0,繼續壓棧。
(4)棧空top=-1,結束程序。判斷是否有回路。
【方法二】使用棧實現
轉載自:https://blog.csdn.net/Akatsuki__Itachi/article/details/80443778
void toposort()
{priority_queue
<int,vector
<int>,greater
<int> >q
;for(int i
=1;i
<=n
;i
++)if(!num
[i
])q
.push(i
);int len
=0;while(!q
.empty()){int u
=q
.top();q
.pop();ans
[len
++]=u
;for(int i
=0;i
<v
[u
].size();i
++){int t
=v
[u
][i
];num
[t
]--;if(!num
[t
])q
.push(t
);}}cout
<<ans
[0];for(int i
=1;i
<len
;i
++)cout
<<" "<<ans
[i
];cout
<<endl
;
}
【方法三】使用隊列實現
轉載自:https://www.cnblogs.com/wkfvawl/p/9129325.html
#include<cstdio>
#include<cstring>
int ans
[510][510];
int n
,indegree
[510];
int queue
[510];
void topsort()
{int i
,j
,top
,k
=0;for(j
=0; j
<n
; ++j
){for(i
=1; i
<=n
; ++i
){if(indegree
[i
]==0){top
=i
;break;}}queue
[k
++]=top
;indegree
[top
]=-1;for(i
=1; i
<=n
; ++i
){if(ans
[top
][i
])indegree
[i
]--;}}for(i
=0; i
<k
-1; ++i
)printf("%d ",queue
[i
]);printf("%d\n",queue
[n
-1]);
}int main()
{int i
,a
,b
,m
;while(scanf("%d%d",&n
,&m
)!=EOF){memset(indegree
,0,sizeof(indegree
));memset(ans
,0,sizeof(ans
));for(i
=0; i
<m
; ++i
){scanf("%d%d",&a
,&b
);if(ans
[a
][b
]==0){ans
[a
][b
]=1;indegree
[b
]++;}}topsort();}return 0;
}
可以采用隊列來存儲入度為0的頂點的原因? 在算法執行過程中,如果同時存在多個入度為0
的頂點,則首先選擇刪除哪個頂點不會影響算法的正確性。所以,在拓撲排序算法中,可以使用隊列或棧來存儲入度為0的頂點。
以上轉自:https://www.cnblogs.com/wkfvawl/p/9876395.html
2. 理論上刪去頂點的所有出邊,實際的工程實現中是否真的刪除邊?如何處理?
利用度數數組減一。
3. 當有多個入度為0的頂點時,能否同時將它們記錄下來并形成一種線性關系?采用什么結構/輔助手段比較好?
也是用鏈式棧記錄前驅。
如果有向圖的拓撲排序序列是唯一的,則圖中必定只有一個頂點的入度為0,一個頂點的出度為0。(錯)
只有一個頂點的入度為0,一個頂點的出度為0,不一定能得出唯一的拓撲排序。這是必要條件而不是充分條件。
其他資料轉載
相關問題
拓撲排序模板: 洛谷P1113 雜務.
Floyed+拓撲排序: 洛谷P2419 USACO08JANCow Contest S.
拓撲排序+思維(NOIP普及組考試題): 洛谷P1983 車站分級.
真·拓撲排序 : 洛谷P1347 排序.
以上轉載自:https://blog.csdn.net/Berserker____/article/details/106733772拓撲排序與BFS
(1)拓撲排序與 BFS 算法的對比分析
與用鄰接表實現 BFS 算法的偽代碼相比,拓撲排序與其有相似之處也有不同之處。
相同點:拓撲排序實質上就是一種廣度優先搜索,在算法執行過程中,通過棧頂頂點訪問它的每個鄰接點,整個算法執行過程中,每個頂點訪問一次且僅一次,每條邊掃描一次且僅一次。
不同點:BFS 算法在掃描每條邊時,如果邊的終點沒有訪問過,則入隊列;而拓撲排序算法在掃描每條邊時,終點的入度要減 1,當減至 0 時才將該終點入棧。
(2)拓撲排序與 BFS 算法棧或隊列的使用
請注意,在BFS 算法中是用隊列來存儲待擴展的頂點,在 拓撲排序算法中是用棧來存儲入度為 0 的頂點。那么,在 BFS 算法中是否可以用棧來存儲待擴展的頂點?在拓撲排序算法中是否可以用隊列來存儲入度為 0 的頂點?隊列和棧的區別在于頂點出隊列(或棧)的順序,隊列是先進先出,棧是后進先出。所以,能否用隊列(或棧)關鍵要看這種順序是否會影響算法的正確性。
BFS 算法:如果用棧存儲待擴展的頂點,如圖所示,其中圖(a)為正確的搜索過程,圖(b)為用棧存儲待擴展頂點時各頂點入棧和出棧的過程。在圖(b)中,依次出棧的頂點是 A→E→D→F→H→I→B→C→G,很明顯,這與 BFS 算法的實現過程和頂點訪問順序大相徑庭。因此,在 BFS算法中不能用棧來存儲待擴展的頂點。
以上轉載自:https://www.cnblogs.com/wkfvawl/p/9876395.html
總結
以上是生活随笔為你收集整理的活动网络——用顶点表示活动的网络AOV和拓扑的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。