拓扑排序
拓扑排序
在实际应用中,有向图的边可以看做是顶点之间制约关系的描述。把顶点看作是一个个任务,则对于有向边<Vi,Vj>表明任务Vj的启动需等到任务Vi完成之后,也就是说任务Vi先于任务Vj完成。对于一个有向图,找出一个顶点序列,且序列满足:若顶点Vi和Vj之间有一条边<Vi,Vj>,则在此序列中顶点Vi必在顶点Vj之前。这样的一个序列就称为有向图的拓扑序列(topological order)。
步骤
- 从有向图中选取一个没有前驱(入度为0)的顶点输出。
- 删除图中所有以它为起点的弧。
重复上述两个步骤,此时会出现两种情形
1.所有顶点都已输出,输出序列就是拓扑序列。
2.已没有无前驱的顶点,但任然有顶点没有输出,这表明该有向图是有环的。
可见拓扑排序可以检测有向图是否有环。
必须指出,即使是存储结构已确定,拓扑序列也不一定是唯一的。拓扑排序序列不仅跟存储结构有关也与具体采用的算法有关。
算法说明
使用一个int型的数组indegree,来表示每个顶点的入度。于是,通过查找数组indegree就可得到前驱为零的顶点。下面的代码采取另一种做法:使用一队列,入度为零的顶点入队。这样每次输出入度为零的顶点时,只需出队即可,不必每次都检测整个indegree数组。
这也说明拓扑序列不是唯一的。若每次都检测indegree数组,则输出的是入度为零的顶点中下标最小的。而使用队列则不一定是这样,它是先入度为零的先输出。
代码
类定义
- #include<iostream>
- #include<iomanip>
- #include<queue>
- using namespace std;
- /*
- 用邻接矩阵实现图
- 拓扑排序必须是有向图
- */
- class Graph
- {
- private:
- //顶点数
- int numV;
- //边数
- int numE;
- //顶点的入度
- int *indegree;
- //邻接矩阵
- int **matrix;
- public:
- Graph(int numV);
- //建图
- void createGraph(int numE);
- //析构方法
- ~Graph();
- //获取顶点数
- int getVerNums()
- {
- return numV;
- }
- //获取边数
- int getEdgeNums()
- {
- return numE;
- }
- //拓扑排序
- void topologicalSort();
- //打印邻接矩阵
- void printAdjacentMatrix();
- //检查输入
- bool check(int, int);
- };
- //构造函数,指定顶点数目
- Graph::Graph(int numV)
- {
- //对输入的顶点数进行检测
- while (numV <= 0)
- {
- cout << "顶点数有误!重新输入 ";
- cin >> numV;
- }
- this->numV = numV;
- //构建邻接矩阵,并初始化
- matrix = new int*[numV];
- int i, j;
- for (i = 0; i < numV; i++)
- matrix[i] = new int[numV];
- //由于权值对于拓扑排序并无作用,故采取无权图的做法
- for (i = 0; i < numV; i++)
- for (j = 0; j < numV; j++)
- matrix[i][j] = 0;
- //构建顶点的入度数组,并初始化
- indegree = new int[numV];
- for (i = 0; i < numV; i++)
- indegree[i] = 0;
- }
- void Graph::createGraph(int numE)
- {
- /*
- 对输入的边数做检测
- 一个numV个顶点的有向图,最多有numV*(numV - 1)条边
- */
- while (numE < 0 || numE > numV*(numV - 1))
- {
- cout << "边数有问题!重新输入 ";
- cin >> numE;
- }
- this->numE = numE;
- int tail, head, i;
- i = 0;
- cout << "输入每条边的起点(弧尾)、终点(弧头)" << endl;
- while (i < numE)
- {
- cin >> tail >> head;
- while (!check(tail, head))
- {
- cout << "输入的边不正确!请重新输入 " << endl;
- cin >> tail >> head;
- }
- matrix[tail][head] = 1;
- indegree[head]++;
- i++;
- }
- }
- Graph::~Graph()
- {
- int i;
- for (i = 0; i < numV; i++)
- delete[] matrix[i];
- delete[]matrix;
- delete[]indegree;
- }
- //拓扑排序
- void Graph::topologicalSort()
- {
- int i, vertex;
- queue<int> q;
- //入度为零的顶点入队
- for (i = 0; i < numV; i++)
- if (indegree[i] == 0)
- q.push(i);
- bool *visited = new bool[numV];
- for (i = 0; i < numV; i++)
- visited[i] = false;
- while (!q.empty())
- {
- vertex = q.front();
- q.pop();
- cout << setw(4) << vertex;
- visited[vertex] = true;
- for (i = 0; i < numV; i++)
- if (matrix[vertex][i] == 1)
- {
- //调整入度,入度为0则需入队
- if (!(--indegree[i]))
- q.push(i);
- }
- }
- cout << endl;
- for (i = 0; i < numV; i++)
- if (!visited[i])
- cout << "该有向图有环!";
- cout << endl;
- delete[]visited;
- }
- //打印邻接矩阵
- void Graph::printAdjacentMatrix()
- {
- int i, j;
- cout.setf(ios::left);
- cout << setw(4) << " ";
- for (i = 0; i < numV; i++)
- cout << setw(4) << i;
- cout << endl;
- for (i = 0; i < numV; i++)
- {
- cout << setw(4) << i;
- for (j = 0; j < numV; j++)
- cout << setw(4) << matrix[i][j];
- cout << endl;
- }
- }
- bool Graph::check(int tail, int head)
- {
- if (tail < 0 || tail >= numV || head < 0 || head >= numV)
- return false;
- return true;
- }
- int main()
- {
- cout << "******拓扑排序***by David***" << endl;
- int numV, numE;
- cout << "建图..." << endl;
- cout << "输入顶点数 ";
- cin >> numV;
- Graph graph(numV);
- cout << "输入边数 ";
- cin >> numE;
- graph.createGraph(numE);
- cout << "打印邻接矩阵..." << endl;
- graph.printAdjacentMatrix();
- cout << "拓扑排序..."<<endl;
- graph.topologicalSort();
- system("pause");
- return 0;
- }
运行
完整代码下载:拓扑排序