拓撲排序

  在圖論中,由一個有向無環圖的頂點組成的序列,當且僅當滿足下列條件時,稱爲該圖的一個拓撲排序(Topological sorting)。
  1)每個頂點出現且只出現一次;
  2)若A在序列中排在B的前面,則在圖中不存在從B到A的路徑。
也可以定義爲:拓撲排序是對有向無環圖的頂點的一種排序,它使得如果存在一條從頂點A到頂點B的路徑,那麼在排序中B出現在A的後面。

  什麼地方會用到拓撲排序呢?比如在存在着前後依賴關係的情況下,整個工程中,有些子工程(活動)必須在其它有關子工程完成之後才能開始,也就是說,一個子工程的開始是以它的所有前序子工程的結束爲先決條件的,但有些子工程沒有先決條件,可以安排在任何時間開始。
  舉個例子:學習《數據結構》課程就必須安排在學完它的兩門先修課程《離散數學》和《算法語言》之後。學習《高等數學》課程則可以隨時安排,因爲它是基礎課程,沒有先修課。

  拓撲排序是對於有向無環圖而言的(DAG),就是對於這個圖所有的點(V1, V2, … Vn)找到一個點序列使得任意邊(u, v), u出現在v的前面。很容易證明,如果一個有向圖中有環那麼不存在拓撲排序。

  1)從 DAG 圖中選擇一個 沒有前驅(即入度爲0)的頂點並輸出。
  2)從圖中刪除該頂點和所有以它爲起點的有向邊。
  3)重複 1 和 2 直到當前的 DAG 圖爲空或當前圖中不存在無前驅的頂點爲止。後一種情況說明有向圖中必然存在環。


……

複雜度分析:
§Initialize In-Degree array:
O(|E|)
§Find vertex with in-degree 0:
|V| vertices, each takes O(|V|) to search In-Degree array.
Total time = O(|V|^2)
§Reduce In-Degree of all vertices adjacent to a vertex:
O(|E|)
§Output and mark vertex:
O(|V|)

Total time = O(|V|^2 + |E|) Quadratic time!

Kahn算法:

Total time = O(|V| + |E|)

#include <iostream>
#include <list>
#include <queue>
#include <vector>
using namespace std;
class Graph{
    int V; //定點數
    list<int>* adj; //鄰接表數組指針
public:
    Graph(int v);
    void AddEdge(int v, int w);
    int GetVecNum();
    list<int>* GetAdj();
};

Graph::Graph(int v):V(v){
    adj = new list<int>[v];
};
void Graph::AddEdge(int v, int w){
    adj[v].push_back(w);
}

int Graph::GetVecNum(){
    return V;
}

list<int>*  Graph::GetAdj(){
    return adj;
}

const int V = 6;
queue<int> zeroInDegreesQueue;
int inDegrees[V];
vector<int> resultVec;

void InitZeroIndegreeQueue(Graph& graph){
    list<int>* adj = graph.GetAdj();
    for(int i=0; i<graph.GetVecNum(); i++){
        for (list<int>::iterator it=adj[i].begin(); it != adj[i].end(); ++it)
            inDegrees[*it]++;
    }
    for(int i=0; i<graph.GetVecNum(); i++){
        if(inDegrees[i] == 0)
            zeroInDegreesQueue.push(i);
    }
}

void KahnTopologicalSort(Graph& graph){
    int item;
    int vecNum = 0;
    list<int>* adj = graph.GetAdj();

    while(!zeroInDegreesQueue.empty()){
        resultVec.push_back(item = zeroInDegreesQueue.front());
        zeroInDegreesQueue.pop();
        vecNum++;
        for (list<int>::iterator it=adj[item].begin(); it != adj[item].end(); ++it){
            if(--inDegrees[*it] == 0)
                zeroInDegreesQueue.push(*it);
        }
    }

    if(vecNum < graph.GetVecNum())
        cout<<"There is a cycle!\n";
}

void printResult(int i){
    cout << ' ' << i;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Graph g(V);
    g.AddEdge(5, 2);
    g.AddEdge(5, 0);
    g.AddEdge(4, 0);
    g.AddEdge(4, 1);
    g.AddEdge(2, 3);
    g.AddEdge(3, 1);

    cout << "Following is a KahnTopological Sort of the given graph \n";

    InitZeroIndegreeQueue(g);
    KahnTopologicalSort(g);

    for_each(resultVec.begin(),resultVec.end(),printResult);


    return 0;
}

DFS:
  深度優先方法,其實可看做逆向的方法,找到沒有出度的點,通過遞歸逆推。
L ← Empty list that will contain the sorted nodes
S ← Set of all nodes with no outgoing edges
for each node n in S do
  visit(n)
function visit(node n)
  if n has not been visited yet then
  mark n as visited
  for each node m with an edge from m to n do
   visit(m)
  add n to L

// A recursive function used by topologicalSort
void Graph::topologicalSortUtil(int v, bool visited[], stack<int> &Stack)
{
    // Mark the current node as visited.
    visited[v] = true;

    // Recur for all the vertices adjacent to this vertex
    list<int>::iterator i;
    for (i = adj[v].begin(); i != adj[v].end(); ++i)
        if (!visited[*i])
            topologicalSortUtil(*i, visited, Stack);

    // Push current vertex to stack which stores result
    Stack.push(v);
}

// The function to do Topological Sort. It uses recursive topologicalSortUtil()
void Graph::topologicalSort()
{
    stack<int> Stack;

    // Mark all the vertices as not visited
    bool *visited = new bool[V];
    for (int i = 0; i < V; i++)
        visited[i] = false;

    // Call the recursive helper function to store Topological Sort
    // starting from all vertices one by one
    for (int i = 0; i < V; i++)
      if (visited[i] == false)
        topologicalSortUtil(i, visited, Stack);

    // Print contents of stack
    while (Stack.empty() == false)
    {
        cout << Stack.top() << " ";
        Stack.pop();
    }
}

// Driver program to test above functions
int main()
{
    // Create a graph given in the above diagram
    Graph g(6);
    g.addEdge(5, 2);
    g.addEdge(5, 0);
    g.addEdge(4, 0);
    g.addEdge(4, 1);
    g.addEdge(2, 3);
    g.addEdge(3, 1);

    cout << "Following is a Topological Sort of the given graph \n";
    g.topologicalSort();

    return 0;
}

參考:
https://zh.wikipedia.org/wiki/%E6%8B%93%E6%92%B2%E6%8E%92%E5%BA%8F
http://www.ivy-end.com/archives/992

發佈了89 篇原創文章 · 獲贊 9 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章