算法筆記---問題 A: 算法7-12:有向無環圖的拓撲排序

題目描述

由某個集合上的一個偏序得到該集合上的一個全序,這個操作被稱爲拓撲排序。偏序和全序的定義分別如下:
若集合X上的關係R是自反的、反對稱的和傳遞的,則稱R是集合X上的偏序關係。
設R是集合X上的偏序,如果對每個x,y∈X必有xRy或yRx,則稱R是集合X上的全序關係。
由偏序定義得到拓撲有序的操作便是拓撲排序。
拓撲排序的流程如下:

  1. 在有向圖中選一個沒有前驅的頂點並且輸出之;
  2. 從圖中刪除該頂點和所有以它爲尾的弧。

重複上述兩步,直至全部頂點均已輸出,或者當前圖中不存在無前驅的頂點爲止。後一種情況則說明有向圖中存在環。
採用鄰接表存儲有向圖,並通過棧來暫存所有入度爲零的頂點,可以描述拓撲排序的算法如下:
在這裏插入圖片描述
在本題中,讀入一個有向圖的鄰接矩陣(即數組表示),建立有向圖並按照以上描述中的算法判斷此圖是否有迴路,如果沒有迴路則輸出拓撲有序的頂點序列。

輸入

輸入的第一行包含一個正整數n,表示圖中共有n個頂點。其中n不超過50。
以後的n行中每行有n個用空格隔開的整數0或1,對於第i行的第j個整數,如果爲1,則表示第i個頂點有指向第j個頂點的有向邊,0表示沒有i指向j的有向邊。當i和j相等的時候,保證對應的整數爲0。

輸出

如果讀入的有向圖含有迴路,請輸出“ERROR”,不包括引號。
如果讀入的有向圖不含有迴路,請按照題目描述中的算法依次輸出圖的拓撲有序序列,每個整數後輸出一個空格。
請注意行尾輸出換行。

樣例
輸入:
4
0 1 0 0
0 0 1 0
0 0 0 0
0 0 1 0
輸出:
3 0 1 2 

解題思路:

在本題中,需要嚴格的按照題目描述中的算法進行拓撲排序,並在排序的過程中將頂點依次儲存下來,直到最終能夠判定有向圖中不包含迴路之後,才能夠進行輸出。
另外,爲了避免重複檢測入度爲零的頂點,可以通過一個棧結構維護當前處理過程中入度爲零的頂點。

使用拓撲排序算法來求解
關鍵是求出每個結點的入度
然後
1、將入度爲0的結點放入棧中,
2、每次從棧中彈出一個結點後,然後將該結點所有連接的結點的入度都減一,
3、若結點的度爲0,則將其加入到棧中。
4、將該結點的所有出邊全部刪掉。
5、重複執行,知道棧爲空
6、判斷拓撲序列的結點數是否等於圖中結點,若等於,則輸出拓撲序列,若不等於,則表示該圖中存在環,返回false

下面爲AC代碼:

/*
 * @Description: 拓撲排序
 * @Author: 
 * @Date: 2020-04-28 20:12:18
 * @LastEditTime: 2020-04-28 21:21:06
 * @LastEditors: Please set LastEditors
 */
#include <iostream>
#include<stack>
#include <vector>
using namespace std;
const int logic_max_num = 50;
vector<int> G[logic_max_num];//鄰接表
int n,in_degree[logic_max_num];//結點個數n,每個結點的入度in_degree
vector<int> res;//拓撲序列

/**
 * @description: 拓撲排序
 * @param : 
 * @return: 返回是否爲拓撲排序
 */
bool to_po_logical_sort()
{
    int num = 0; //記錄加入拓撲排序的頂點數
    stack<int> s;
    for (int i = 0; i < n; i++)
    {
        if (in_degree[i] == 0)
        {
            s.push(i); //將所有入度爲0的點加入棧
        }
    }
    while (!s.empty())
    {
        int top = s.top();
        res.push_back(top);
        s.pop();
        for (int i = 0; i < G[top].size(); i++)
        {
            int v = G[top][i];
            in_degree[v]--;
            if (in_degree[v] == 0)
            {
                s.push(v);
            }
        }
        G[top].clear();
        num++;
    }
    if (num == n)
    {
        return true;
    }
    return false; //表示有環
}

int main()
{
    cin >> n;
    int data;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            cin>>data;
            if(data != 0){
                G[i].push_back(j);
            }
        }
    }
    //求出 in_degree 數組
    for (int i = 0; i < n; i++)
    {
        in_degree[i] = 0;
        for(int j = 0;j < n;j++){
            for(int k = 0;k < G[j].size();k++){
                if(G[j][k] == i){
                    in_degree[i]++;
                }
            }
        }
    }
    bool flag = to_po_logical_sort();
    if (flag)
    {
        for (vector<int>::iterator it = res.begin(); it != res.end(); it++)
        {
            cout << (*it) << " ";
        }
    }
    else
    {
        cout << "ERROR";
    }
    cout << endl;
    system("pause");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章