1. 程式人生 > >有向圖_拓撲排序_AOE關鍵路徑_判斷圖中是否存在環

有向圖_拓撲排序_AOE關鍵路徑_判斷圖中是否存在環

目錄

0.圖——判環

0.1.1無向圖判斷是否存在環

0.1.1無向圖,若深度優先遍歷過程中遇到回邊(即指向已訪問過的頂點的邊),則該無向圖必定存在環路。

參考圖的關節點與重連通分量,圖的深度優先生成樹。

定義visited[]陣列為深度優先遍歷時訪問連通圖的頂點v的序號。

如果某個頂點的visited值比其鄰邊的頂點的visited的值要大,說明,該頂點是後訪問到了,存在一條回邊連線到了其祖先節點!

因為對於任意一個頂點v而言,其孩子節點為在它之後才訪問的節點,而其雙親結點和又回邊連線的祖先結點是在它之前就訪問過的結點!

0.1.2有向圖判斷是否存在環

0.2.1對於有向圖,利用拓撲排序來判斷是否存在環:

對於無環的有向圖,對其進行拓撲排序可輸出其所有頂點,而有環的圖則不行!

0.2無向圖判環

//用鄰接表來存圖
//先定義圖的頂點資料結構,
typedef struct Node
{
	int num;//頂點編號
	char Alphabet;
	//int visited;
}Node;


//無向圖的鄰接表表示,每個頂點對應一個列表
class UDGALGraph
{
private:
	vector<Node*> vecNodes;//圖的結點地址向量,依次存每一個結點的地址
	vector<vector<Node*>*> vexLists;//儲存圖中有向邊的資訊:每個頂點有一個鄰接表,該鄰接表上依次掛有其鄰接頂點的地址
	int vexNum, edgeNum;
	vector<int> visitedOrder;

public:
	UDGALGraph()
	{
		CreateUDG();
	}
	~UDGALGraph()
	{
		DestroyDUG();
	}
	void CreateUDG();
	void DestroyDUG();

	void DFS(int v,int& count,bool& hasLoop);
	//從圖al的頂點v出發,遞迴地深度優先遍歷圖G

    bool DFSTraverse();

	bool hasLoop();

};

無向圖利用深度優先遍歷來判斷是否存在環:


	void DFS(int v,int& count,bool& hasLoop)
	{//從圖al的頂點v出發,遞迴地深度優先遍歷圖G
		visitedOrder[v] = count++;
		cout << vecNodes[v]->Alphabet << " ";
		for (int i = 0; i < (vexLists[v]->size()); i++)
		{
			int nodeNum = vexLists[v]->at(i)->num;
			if (visitedOrder[nodeNum] == 0)
			{
				DFS(nodeNum, count, hasLoop);
			}
			else if (visitedOrder[nodeNum]>visitedOrder[v])
			{
				hasLoop = true;
			}
		}
	}


	bool DFSTraverse()
	{
		int count = 1;
		bool hasLoop = false;
		for (int i = 0; i < vexNum; i++)
		{
			if (visitedOrder[i] == 0)
			{
				DFS(i, count, hasLoop);
			}
		}
		/*
		algraph是在堆中分配的結點,每次範圍後,其每一個結點的標誌位都設定為1了,退出時,
		下次再遍歷前要清一下標誌位。
		*/
		for (int i = 0; i < vexNum; i++)
		{
			visitedOrder[i] = 0;
		}
		return hasLoop;
	}
	bool hasLoop()
	{//對於無向圖來說,若深度優先遍歷過程中遇到回邊(即連線已經訪問過的頂點的鄰邊),則說明該無向圖存在環!
		if (DFSTraverse())
		{
			return true;
		}
		else
		{
			return false;
		}
	}

0.3有向圖判環



//有向圖的鄰接表表示,每個頂點對應一個鄰接連結串列
class DGALGraph
{
private:
	vector<Node*> vecNodes;//圖的結點地址向量,依次存每一個結點的地址
	vector<vector<Node*>*> vexLists;//儲存圖中有向邊的資訊:每個頂點有一個鄰接表,該鄰接表上依次掛有其鄰接頂點的地址
	int vexNum, edgeNum;
	vector<int> InDegree;
	vector<int> visited;
	deque<int> topologicalSequence;//有向無環圖的拓撲序列

public:
	DGALGraph()
	{
		CreateUDG();
	}
	~DGALGraph()
	{
		DestroyDUG();
	}
	void CreateUDG();
	
	void DestroyDUG();

	bool topologicalSort();
	bool hasLoop();
	void printTopoSeq()
};

有向圖利用拓撲排序來判斷是否存在環:

	bool topologicalSort()
	{
		cout << "有向圖的拓撲排序:" << endl;
		stack<int> inDegree0VexStack;
		for (int i = 0; i < vexNum; i++)
		{
			if (InDegree[i] == 0)
			{
				inDegree0VexStack.push(i);
			}
		}
		int count = 0;//對輸出頂點計數
		while (!inDegree0VexStack.empty())
		{
			int i = inDegree0VexStack.top();//輸入i號頂點,並計數
			inDegree0VexStack.pop();
			//cout << vecNodes[i]->Alphabet << " ";
			cout << i + 1 << " ";
			++count;
			for (int j = 0; j < vexLists[i]->size(); j++)
			{//對i號頂點的每一個鄰接頂點j的入度減1,即i->i的鄰接頂點   
				int nodeNum = vexLists[i]->at(j)->num;
				if ((--InDegree[nodeNum] == 0))
				{//若入度減到了0,則入棧
					inDegree0VexStack.push(nodeNum);
				}

			}
		}
		if (count < vexNum)
		{//該有向圖有環
			return true;
		}
		else
		{//該有向圖無環,可將所有頂點按拓撲有序輸出。
			return false;
		}
	}

	bool hasLoop()
	{
		if (topologicalSort())
		{
			cout << endl; 
			cout << "該有向圖有環!" << endl;
			return true;
		}
		else
		{
			cout << endl;
			cout << "該有向圖無環!" << endl;
			return false;
		}
	}//topologicalSort

1.有向圖的拓撲排序

2.關鍵路徑

 2.1.鄰接連結串列儲存AOE網


typedef struct ArcNode{
	int v1;	//弧尾
	int v2;//弧頭:該弧指向的頂點的位置
	int weight;	//資料域
}ArcNode;

typedef struct VNode{
	int vexNo;	//頂點編號
}VNode;


class AOEALGraph{
private://用鄰接連結串列儲存AOE網!
	int vexNum, arcNum;
	vector<VNode*> nodesPtrVector;
	vector<vector<ArcNode*>*> nodeArcPtrVector;//每個頂點的出弧指標向量
	stack<int> ReverseTopoOrder;//逆拓撲排序
	vector<int> InDegree;//各個頂點的入度
	vector<int> ve;//各個頂點的最早發生時間向量
	vector<int> vl;//各個頂點的最晚發生時間向量
	vector<ArcNode*> CP;//關鍵路徑!存弧,即活動

public:
    	AOEALGraph()
	{
		CreateAOE();
	}
	void CreateAOE();

	bool TopologicalOrder();

	bool CriticalPath()

};

 2.1.1拓撲排序: bool TopologicalOrder():

	bool TopologicalOrder()
	{
		int count=0;
		stack<int> _0InDegreeStack;
		for (int i = 0; i < vexNum; i++)
		{
			if (InDegree[i] == 0)
			{
				_0InDegreeStack.push(i);
			}
		}
		while (!_0InDegreeStack.empty())
		{
			int j = _0InDegreeStack.top();
			_0InDegreeStack.pop();
			ReverseTopoOrder.push(j);//j號頂點入逆拓撲排序棧!
			count++;
			for (int i = 0; i < nodeArcPtrVector[j]->size(); i++)
			{//遍歷j號頂點的所有鄰接點k
				int k = nodeArcPtrVector[j]->at(i)->v2;
				if (--InDegree[k] == 0)
				{//對j號頂點的每一個鄰接頂點的入度-1,若入度為0則入0入度棧
					_0InDegreeStack.push(k);
				}
				if (ve[j] + nodeArcPtrVector[j]->at(i)->weight > ve[k])
				{
					ve[k] = ve[j] + nodeArcPtrVector[j]->at(i)->weight;
				}
			}
		}
		if (count < vexNum)
		{
			return false;
		}
		else
		{
			return true;
		}
	}

2.1.2關鍵路徑:bool CriticalPath()

	bool CriticalPath()
	{
		if (!TopologicalOrder())
		{
			return false;
		}
		for (int i = 0; i < vexNum; i++)
		{//初始化各個頂點事件的最遲發生時間!
			vl[i] = ve[vexNum - 1];
		}

		while (!ReverseTopoOrder.empty())//按拓撲逆序求各個頂點的vl值
		{

			int j = ReverseTopoOrder.top();
			ReverseTopoOrder.pop();
			for (int i = 0; i < nodeArcPtrVector[j]->size(); i++)
			{
				int k = nodeArcPtrVector[j]->at(i)->v2;
				int dut = nodeArcPtrVector[j]->at(i)->weight;
				if (vl[k] - dut < vl[j])
				{
					vl[j] = vl[k] - dut;
				}
			}//for
		}
		for (int j = 0; j < vexNum; j++)
		{//求沒一條弧的最早開始時間ee(i)和最晚開始時間el(i),若ee=el則為關鍵活動!將關鍵活動的這條弧的指標存入CP向量
			for (int i = 0; i < nodeArcPtrVector[j]->size(); i++)
			{
				int k = nodeArcPtrVector[j]->at(i)->v2;
				int dut = nodeArcPtrVector[j]->at(i)->weight;
				int ee = ve[j];
				int el = vl[k] - dut;
				if (ee == el)
				{
					CP.push_back(nodeArcPtrVector[j]->at(i));
				}
			}
		}
		//輸出關鍵活動弧
		for (int i = 0; i < CP.size(); i++)
		{
			cout << CP[i]->v1 << "->" << CP[i]->v2 << ",weight=" << CP[i]->weight << endl;
		}
		return true;
	}

求關鍵路徑的完整程式碼與測試用例:

// CriticalPath2.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include<iostream>
#include<vector>
#include<stack>
using namespace std;

typedef struct ArcNode{
	int v1;	//弧尾
	int v2;//弧頭:該弧指向的頂點的位置
	int weight;	//資料域
}ArcNode;

typedef struct VNode{
	int vexNo;	//頂點編號
}VNode;


class AOEALGraph{
private://用鄰接連結串列儲存AOE網!
	int vexNum, arcNum;
	vector<VNode*> nodesPtrVector;
	vector<vector<ArcNode*>*> nodeArcPtrVector;//每個頂點的出弧指標向量
	stack<int> ReverseTopoOrder;//逆拓撲排序
	vector<int> InDegree;//各個頂點的入度
	vector<int> ve;//各個頂點的最早發生時間向量
	vector<int> vl;//各個頂點的最晚發生時間向量
	vector<ArcNode*> CP;//關鍵路徑!存弧,即活動

public:

	AOEALGraph()
	{
		CreateAOE();
	}
	void CreateAOE()
	{
		cin >> vexNum >> arcNum;
		for (int i = 0; i < vexNum; i++)
		{
			VNode* vexNodePtr = new VNode;
			vexNodePtr->vexNo = i;
			nodesPtrVector.push_back(vexNodePtr);

			vector<ArcNode*>* arcALPtr = new vector < ArcNode* > ;
			nodeArcPtrVector.push_back(arcALPtr);

			InDegree.push_back(0);
			ve.push_back(0);
			vl.push_back(0);
		}
		int v1, v2, weight;
		int v1No, v2No;
		for (int i = 0; i < arcNum; i++)
		{
			cin >> v1 >> v2 >> weight;
			v1No = v1 - 1;
			v2No = v2 - 1;
			ArcNode* arcNodePtr = new ArcNode;
			arcNodePtr->v1 = v1No;
			arcNodePtr->v2 = v2No;
			arcNodePtr->weight = weight;
			nodeArcPtrVector[v1No]->push_back(arcNodePtr);
			InDegree[v2No]++;//頂點V2的入度+1
		}

	}

	bool TopologicalOrder()
	{
		int count=0;
		stack<int> _0InDegreeStack;
		for (int i = 0; i < vexNum; i++)
		{
			if (InDegree[i] == 0)
			{
				_0InDegreeStack.push(i);
			}
		}
		while (!_0InDegreeStack.empty())
		{
			int j = _0InDegreeStack.top();
			_0InDegreeStack.pop();
			ReverseTopoOrder.push(j);//j號頂點入逆拓撲排序棧!
			count++;
			for (int i = 0; i < nodeArcPtrVector[j]->size(); i++)
			{//遍歷j號頂點的所有鄰接點k
				int k = nodeArcPtrVector[j]->at(i)->v2;
				if (--InDegree[k] == 0)
				{//對j號頂點的每一個鄰接頂點的入度-1,若入度為0則入0入度棧
					_0InDegreeStack.push(k);
				}
				if (ve[j] + nodeArcPtrVector[j]->at(i)->weight > ve[k])
				{
					ve[k] = ve[j] + nodeArcPtrVector[j]->at(i)->weight;
				}
			}
		}
		if (count < vexNum)
		{
			return false;
		}
		else
		{
			return true;
		}
	}

	bool CriticalPath()
	{
		if (!TopologicalOrder())
		{
			return false;
		}
		for (int i = 0; i < vexNum; i++)
		{//初始化各個頂點事件的最遲發生時間!
			vl[i] = ve[vexNum - 1];
		}

		while (!ReverseTopoOrder.empty())//按拓撲逆序求各個頂點的vl值
		{

			int j = ReverseTopoOrder.top();
			ReverseTopoOrder.pop();
			for (int i = 0; i < nodeArcPtrVector[j]->size(); i++)
			{
				int k = nodeArcPtrVector[j]->at(i)->v2;
				int dut = nodeArcPtrVector[j]->at(i)->weight;
				if (vl[k] - dut < vl[j])
				{
					vl[j] = vl[k] - dut;
				}
			}//for
		}
		for (int j = 0; j < vexNum; j++)
		{//求沒一條弧的最早開始時間ee(i)和最晚開始時間el(i),若ee=el則為關鍵活動!將關鍵活動的這條弧的指標存入CP向量
			for (int i = 0; i < nodeArcPtrVector[j]->size(); i++)
			{
				int k = nodeArcPtrVector[j]->at(i)->v2;
				int dut = nodeArcPtrVector[j]->at(i)->weight;
				int ee = ve[j];
				int el = vl[k] - dut;
				if (ee == el)
				{
					CP.push_back(nodeArcPtrVector[j]->at(i));
				}
			}
		}
		//輸出關鍵活動弧
		for (int i = 0; i < CP.size(); i++)
		{
			cout << CP[i]->v1 << "->" << CP[i]->v2 << ",weight=" << CP[i]->weight << endl;
		}
		return true;
	}

};

int _tmain(int argc, _TCHAR* argv[])
{
	AOEALGraph aoe;
	aoe.CriticalPath();

	system("pause");
	return 0;
}


/*
9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
7 9 2
8 9 4
0->1,weight=6
1->4,weight=1
4->6,weight=9
4->7,weight=7
6->8,weight=2
7->8,weight=4
請按任意鍵繼續. . .




6 8
1 2 3
1 3 2
2 4 2
2 5 3
3 4 4
3 6 3
4 6 2
5 6 1
0->2,weight=2
2->3,weight=4
3->5,weight=2
請按任意鍵繼續. . .


*/

無向圖、有向圖判環的完整程式碼和測試用例

// TopoSort.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"
#include<iostream>
#include<vector>
#include<stack>
#include<deque>

using namespace std;

//用鄰接表來存圖
//先定義圖的頂點資料結構,
typedef struct Node
{
	int num;//頂點編號
	char Alphabet;
	//int visited;
}Node;


//無向圖的鄰接表表示,每個頂點對應一個列表
class UDGALGraph
{
private:
	vector<Node*> vecNodes;//圖的結點地址向量,依次存每一個結點的地址
	vector<vector<Node*>*> vexLists;//儲存圖中有向邊的資訊:每個頂點有一個鄰接表,該鄰接表上依次掛有其鄰接頂點的地址
	int vexNum, edgeNum;
	vector<int> visitedOrder;

public:
	UDGALGraph()
	{
		CreateUDG();
	}
	~UDGALGraph()
	{
		DestroyDUG();
	}
	void CreateUDG()
	{
		cout << "請輸入無向圖的頂點個數和邊數目,然後依次輸入各條邊的兩個頂點資訊:" << endl;
		cin >> vexNum >> edgeNum;
		for (int i = 0; i < vexNum; i++)
		{
			Node* nodePtr = new Node;
			nodePtr->num = i;
			nodePtr->Alphabet = 'A' + i;
			//nodePtr->visited = 0;
			vecNodes.push_back(nodePtr);

			vector<Node*>* vexVecPtr = new vector < Node* >;
			vexLists.push_back(vexVecPtr);

			visitedOrder.push_back(0);
		}


		char VexAlphabet1, VexAlphabet2;
		int vexNo1, vexNo2;
		for (int i = 0; i < edgeNum; i++)
		{
			cin >> VexAlphabet1 >> VexAlphabet2;
			vexNo1 = VexAlphabet1 - 'A';
			vexNo2 = VexAlphabet2 - 'A';

			//無向圖,在鄰接表中存雙向邊
			vexLists[vexNo1]->push_back(vecNodes[vexNo2]);
			vexLists[vexNo2]->push_back(vecNodes[vexNo1]);
		}
	}

	void DestroyDUG()
	{
		for (int i = 0; i < vexNum; i++)
		{
			delete vecNodes[i];
			vecNodes[i] = nullptr;
		}
		for (int i = 0; i < vexLists.size(); i++)
		{
			delete vexLists[i];
		}
	}


	void DFS(int v,int& count,bool& hasLoop)
	{//從圖al的頂點v出發,遞迴地深度優先遍歷圖G
		visitedOrder[v] = count++;
		cout << vecNodes[v]->Alphabet << " ";
		for (int i = 0; i < (vexLists[v]->size()); i++)
		{
			int nodeNum = vexLists[v]->at(i)->num;
			if (visitedOrder[nodeNum] == 0)
			{
				DFS(nodeNum, count, hasLoop);
			}
			else if (visitedOrder[nodeNum]>visitedOrder[v])
			{
				hasLoop = true;
			}
		}
	}


	bool DFSTraverse()
	{
		int count = 1;
		bool hasLoop = false;
		for (int i = 0; i < vexNum; i++)
		{
			if (visitedOrder[i] == 0)
			{
				DFS(i, count, hasLoop);
			}
		}
		/*
		algraph是在堆中分配的結點,每次範圍後,其每一個結點的標誌位都設定為1了,退出時,
		下次再遍歷前要清一下標誌位。
		*/
		for (int i = 0; i < vexNum; i++)
		{
			visitedOrder[i] = 0;
		}
		return hasLoop;
	}
	bool hasLoop()
	{//對於無向圖來說,若深度優先遍歷過程中遇到回邊(即連線已經訪問過的頂點的鄰邊),則說明該無向圖存在環!
		if (DFSTraverse())
		{
			return true;
		}
		else
		{
			return false;
		}
	}
};


//有向圖的鄰接表表示,每個頂點對應一個鄰接連結串列
class DGALGraph
{
private:
	vector<Node*> vecNodes;//圖的結點地址向量,依次存每一個結點的地址
	vector<vector<Node*>*> vexLists;//儲存圖中有向邊的資訊:每個頂點有一個鄰接表,該鄰接表上依次掛有其鄰接頂點的地址
	int vexNum, edgeNum;
	vector<int> InDegree;
	vector<int> visited;
	deque<int> topologicalSequence;//有向無環圖的拓撲序列

public:
	DGALGraph()
	{
		CreateUDG();
	}
	~DGALGraph()
	{
		DestroyDUG();
	}
	void CreateUDG()
	{
		cout << "請輸入有向圖的頂點個數和邊數目,然後依次輸入各條邊的兩個頂點資訊:" << endl;
		cin >> vexNum >> edgeNum;
		for (int i = 0; i < vexNum; i++)
		{
			Node* nodePtr = new Node;
			nodePtr->num = i;
			nodePtr->Alphabet = 'A' + i;
			//nodePtr->visited = 0;
			vecNodes.push_back(nodePtr);

			vector<Node*>* vexVecPtr = new vector < Node* >;
			vexLists.push_back(vexVecPtr);
			visited.push_back(0);
			InDegree.push_back(0);
		}

		char VexAlphabet1, VexAlphabet2;
		int vexNo1, vexNo2;
		for (int i = 0; i < edgeNum; i++)
		{
			cin >> VexAlphabet1 >> VexAlphabet2;
			vexNo1 = VexAlphabet1 - 'A';
			vexNo2 = VexAlphabet2 - 'A';

			//有向圖
			vexLists[vexNo1]->push_back(vecNodes[vexNo2]);
			InDegree[vexNo2]++;//
		}
	}

	void DestroyDUG()
	{
		for (int i = 0; i < vexNum; i++)
		{
			delete vecNodes[i];
			vecNodes[i] = nullptr;
		}
		for (int i = 0; i < vexLists.size(); i++)
		{
			delete vexLists[i];
		}
	}


	bool topologicalSort()
	{
		cout << "有向圖的拓撲排序:" << endl;
		stack<int> inDegree0VexStack;
		for (int i = 0; i < vexNum; i++)
		{
			if (InDegree[i] == 0)
			{
				inDegree0VexStack.push(i);
			}
		}
		int count = 0;//對輸出頂點計數
		while (!inDegree0VexStack.empty())
		{
			int i = inDegree0VexStack.top();//輸入i號頂點,並計數
			inDegree0VexStack.pop();
			//cout << vecNodes[i]->Alphabet << " ";
			cout << i + 1 << " ";
			++count;
			for (int j = 0; j < vexLists[i]->size(); j++)
			{//對i號頂點的每一個鄰接頂點j的入度減1,即i->i的鄰接頂點   
				int nodeNum = vexLists[i]->at(j)->num;
				if ((--InDegree[nodeNum] == 0))
				{//若入度減到了0,則入棧
					inDegree0VexStack.push(nodeNum);
				}

			}
		}
		if (count < vexNum)
		{//該有向圖有環
			return true;
		}
		else
		{//該有向圖無環,可將所有頂點按拓撲有序輸出。
			return false;
		}
	}

	bool hasLoop()
	{
		if (topologicalSort())
		{
			cout << endl; 
			cout << "該有向圖有環!" << endl;
			return true;
		}
		else
		{
			cout << endl;
			cout << "該有向圖無環!" << endl;
			return false;
		}
	}//topologicalSort



	void DFS(int v)
	{//從圖al的頂點v出發,遞迴地深度優先遍歷圖G
		visited[v] = 1;
		cout << vecNodes[v]->Alphabet << " ";
		for (int i = 0; i < (vexLists[v]->size()); i++)
		{
			int nodeNum = vexLists[v]->at(i)->num;
			if (visited[nodeNum] == 0)
			{
				DFS(nodeNum);
			}
		}
		topologicalSequence.push_front(v);
	}
	//對於有向無環圖,也可以使用深度優先遍歷來求其拓撲序列!
	//最先退出DFS函式的的頂點即出度為0的頂點,是拓撲排序序列中的最後一個頂點,
	//按最先退出DFS函式的先後順序記錄下來的序列就是拓撲排序的逆序序列。
	//類似於有向圖的強連通分量時的finish陣列,用以記錄退出DFS函式的先後順序!
	//用一個雙端佇列來儲存,就可以順序輸出!
	void DFSTraverse()
	{
		for (int i = 0; i < vexNum; i++)
		{
			if (visited[i] == 0)
			{
				DFS(i);
			}
		}
		/*
		algraph是在堆中分配的結點,每次範圍後,其每一個結點的標誌位都設定為1了,退出時,
		下次再遍歷前要清一下標誌位。
		*/
		for (int i = 0; i < vexNum; i++)
		{
			visited[i] = 0;
		}
	}

	void printTopoSeq()
	{
		cout << endl;
		cout << "拓撲序列為:" << endl;
		for (int i = 0; i < topologicalSequence.size(); i++)
		{
			cout << vecNodes[topologicalSequence[i]]->Alphabet << " ";
		}
		cout << endl;
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	//UDGALGraph udg;
	//if (udg.hasLoop())
	//{
	//	cout << "有環" << endl;
	//}
	//else
	//{
	//	cout << "無環" << endl;
	//}

	//DGALGraph dg;
	//dg.hasLoop();


	DGALGraph dg2;
	dg2.DFSTraverse();
	dg2.printTopoSeq();


	system("pause");
	return 0;
}



/*
無向有環圖1:
A-B
A-D
B-C
C-A

4 4
A B
A D
B C
C A

請輸入無向圖的頂點個數和邊數目,然後依次輸入各條邊的兩個頂點資訊:
4 4
A B
A D
B C
C A
A B C D 有環
請按任意鍵繼續. . .

無向無環圖2:
4 3
A B
A D
B C

請輸入無向圖的頂點個數和邊數目,然後依次輸入各條邊的兩個頂點資訊:
4 3
A B
A D
B C
A B C D 無環
請按任意鍵繼續. . .


*/


/*
有向無環圖:
6 8
A B
A C
A D
C B
C E
D E
F D 
F E


請輸入無向圖的頂點個數和邊數目,然後依次輸入各條邊的兩個頂點資訊:
6 8
A B
A C
A D
C B
C E
D E
F D
F E
有向圖的拓撲排序:
6 1 4 3 5 2
該有向圖無環!
請按任意鍵繼續. . .


*/

資料結構有向圖_拓撲排序_AOE關鍵路徑  圖判環