目录
一、拓扑排序的思想
二、代码实现(C++)
代码思想
核心代码
完整代码
一、拓扑排序的思想
        以西红柿炒鸡蛋这道菜为例,其中的做饭流程为:
中间2 6 3 7 4的顺序都可以任意调换,但1和5必须在最前面,这是饭前准备,8必须在最后面。1和5的入度为0,出度为1,8的入度都2,出度为0
在这个操作流程内,把每个步骤当作一个顶点,排序连接起来就是个有向图。
排序,都是将元素按照一定的顺序/规则排列
拓扑排序就是将元素按照先后顺序进行排序
书面解释是:在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为 AOV网(Activity On VertexNetwork)。AOV 网中的弧表示活动之间存在的某种制约关系。
特点是:有向无环(无回路)
拓扑序列:设G=(V,E)是一个具有 n个顶点的有向图V中的顶点序列 V1,V2····满足若从顶点到有一条路径,则在顶点序列中顶点必在顶点之前。则我们称这样的顶点序列为一个拓扑序列。
拓扑排序就是对一个有向图构造拓扑序列的过程。构造时会有两个结果,如果此网的全部顶点都被输出,则说明它是不存在环(回路)的AOV 网,如果输出顶点数少了,哪怕是少了一个,也说明这个网存在环(回路),不是AOV 网
拓扑排序的价值在于,不存在回路的AOV 网,我们可以将它应用在各种各样的工程或项目的流程图中,满足各种应用场景的需要。
二、代码实现(C++)
代码思想
从入度为0的顶点开始访问,访问完成后,将以此顶点为狐尾的弧删除(此顶点的邻接表中的顶点入度都减少1),然后继续查找剩余顶点中入度为0的顶点,重复操作,直到所有顶点都被访问完,或者没有了入度为0的顶点(说明此AOV有回路)
标记V1是从哪个顶点来的,借助栈来存储入度为0的顶点,栈的思想是先入后出
需要借助邻接表来存储图,邻接表内添加一个属性:顶点的入度,当创建图时,路径中每添加一条边时,就将入度+1,如果是v0->b1,则v1的度+1
先遍历度为0的节点,将其入栈,对栈顶元素在邻接表内查找其下一个顶点,并将下个顶点的度-1,设置为0。直到以该顶点所在的邻接表内的顶点的度都设置为0时,即代表将该顶点的狐尾删除。
以该图为例,一张电影制作图,实现其中节点之间关联的排序

核心代码
int Graph::GetVertexIndex(char v)//获取顶点所在的下标
{
	int i;
	for (i = 0; i < m_NumVertex; i++)
	{
		if (m_VerArr[i].m_value == v)
			return i;
	}
	return -1;
}
void Graph::InsertVertex(char v)//插入顶点
{
	if (m_NumVertex >= m_MaxVertex)
		return;
	m_VerArr[m_NumVertex++].m_value = v;
}
void Graph::InsertEdge(char v1, char v2) //插入边
{
	int p1index = GetVertexIndex(v1);
	int p2index = GetVertexIndex(v2);
	if (p1index == -1 || p2index == -1)
		return;
	//v1-v2
	Edge* p = new Edge(p2index);
	p->m_next = m_VerArr[p1index].m_list;
	m_VerArr[p1index].m_list = p;
	m_VerArr[p2index].m_verIn++;
}
void Graph::TopologicalSort()//拓扑排序
{
	stack<int> ss;
	int i;
	Edge* p = NULL;
	for (i = 0; i < m_NumVertex; i++) //将入度为0的顶点入栈
	{
		if (m_VerArr[i].m_verIn == 0)
			ss.push(i);
	}
	for (i = 0; i < m_NumVertex; i++)//循环访问所有顶点进行拓扑排序
//该循环结束的条件:1.循环完,没有度为0的顶点再入栈即栈为空时,退出循环
	{
		if (ss.empty())
		{
			cout << "有回路" << endl;
			return;
		}
		else
		{
			int top = ss.top();//获取栈顶元素并出栈
			ss.pop();
			cout << m_VerArr[top].m_value << " ";//输出
			p = m_VerArr[top].m_list;//让P指向刚出来顶点的邻接表
			while (p)//循环遍历邻接表,设置入度-1
			{
				int in = --m_VerArr[p->m_destindex].m_verIn;//in为p所指向顶点的入度-1
				if (in == 0)
				{
					ss.push(p->m_destindex);//入度为0时,说明顶点就是入读为0的顶点,对其下标入栈
				}
				p = p->m_next;
			}
		}
	}
	cout << endl;
}完整代码
#include<iostream>
using namespace std;
#define SIZE 20
class Edge //边
{
public:
	Edge() :m_next(NULL) {}
	Edge(int index) :m_destindex(index), m_next(NULL) {}
	int m_destindex;
	Edge* m_next;
};
class Vertex //顶点
{
public:
	Vertex() :m_list(NULL),m_verIn(0) {}
	Vertex(char v) :m_value(v), m_list(NULL),m_verIn(0) {}
	char m_value;
	Edge* m_list;
	int m_verIn;
};
class Graph
{
public:
	Graph();
	~Graph();
	void InsertVertex(char v);
	void InsertEdge(char v1, char v2);
	int GetVertexIndex(char v);
	void ShowGraph();
	void TopologicalSort();
private:
	int m_MaxVertex;
	int m_NumVertex;
	//Vertex* m_VerArr;
	Vertex m_VerArr[SIZE];
};
#include<stack>
void Graph::TopologicalSort()//拓扑排序
{
	stack<int> ss;
	int i;
	Edge* p = NULL;
	for (i = 0; i < m_NumVertex; i++)
	{
		if (m_VerArr[i].m_verIn == 0)
			ss.push(i);
	}
	for (i = 0; i < m_NumVertex; i++)
	{
		if (ss.empty())
		{
			cout << "有回路" << endl;
			return;
		}
		else
		{
			int top = ss.top();
			ss.pop();
			cout << m_VerArr[top].m_value << " ";
			p = m_VerArr[top].m_list;
			while (p)
			{
				int in = --m_VerArr[p->m_destindex].m_verIn;
				if (in == 0)
				{
					ss.push(p->m_destindex);
				}
				p = p->m_next;
			}
		}
	}
	cout << endl;
}
void Graph::ShowGraph()
{
	Edge* p = NULL;
	for (int i = 0; i < m_NumVertex; i++)
	{
		cout << i << " " <<m_VerArr[i].m_verIn<<" "<< m_VerArr[i].m_value << "->";
		p = m_VerArr[i].m_list;
		while (p != NULL)
		{
			cout << p->m_destindex << "->";
			p = p->m_next;
		}
		cout << "nul" << endl;
	}
}
int Graph::GetVertexIndex(char v)
{
	int i;
	for (i = 0; i < m_NumVertex; i++)
	{
		if (m_VerArr[i].m_value == v)
			return i;
	}
	return -1;
}
void Graph::InsertVertex(char v)
{
	if (m_NumVertex >= m_MaxVertex)
		return;
	m_VerArr[m_NumVertex++].m_value = v;
}
Graph::Graph()
{
	m_MaxVertex = SIZE;
	m_NumVertex = 0;
	//m_VerArr = new Vertex[m_MaxVertex];
}
Graph::~Graph()
{
	/*	if (m_VerArr != NULL)
		{
			delete[]m_VerArr;
			m_VerArr = NULL;
		}*/
	m_NumVertex = 0;
}
void Graph::InsertEdge(char v1, char v2)
{
	int p1index = GetVertexIndex(v1);
	int p2index = GetVertexIndex(v2);
	if (p1index == -1 || p2index == -1)
		return;
	//v1-v2
	Edge* p = new Edge(p2index);
	p->m_next = m_VerArr[p1index].m_list;
	m_VerArr[p1index].m_list = p;
	m_VerArr[p2index].m_verIn++;
}
void main()
{
	Graph g;//构建一个图
	g.InsertVertex('a');
	g.InsertVertex('b');
	g.InsertVertex('c');
	g.InsertVertex('d');
	g.InsertVertex('e');
	g.InsertVertex('f');
	g.InsertVertex('g');
	g.InsertVertex('h');
	g.InsertVertex('i');
	g.InsertVertex('j');
	g.InsertVertex('l');
	g.InsertVertex('m');
	g.InsertVertex('n');
	g.InsertVertex('o');
	g.InsertVertex('p');
	g.InsertVertex('q');
	g.InsertVertex('r');
	g.InsertEdge('a', 'b');
	g.InsertEdge('b', 'c');
	g.InsertEdge('b', 'd');
	g.InsertEdge('b', 'e');
	g.InsertEdge('c', 'f');
	g.InsertEdge('c', 'g');
	g.InsertEdge('d', 'g');
	g.InsertEdge('d', 'h');
	g.InsertEdge('e', 'h');
	g.InsertEdge('f', 'i');
	g.InsertEdge('g', 'i');
	g.InsertEdge('h', 'i');
	g.InsertEdge('i', 'j');
	g.InsertEdge('i', 'l');
	g.InsertEdge('j', 'm');
	g.InsertEdge('l', 'n');
	g.InsertEdge('l', 'm');
	g.InsertEdge('m', 'o');
	g.InsertEdge('m', 'p');
	g.InsertEdge('n', 'p');
	g.InsertEdge('o', 'q');
	g.InsertEdge('p', 'r');
	g.InsertEdge('q', 'r');
	g.ShowGraph();
	g.TopologicalSort();
}对各顶点和边的添加:第一列是顶点,第二列是计算所有顶点的入度情况,第三列是邻接表




















