-----装载自神奕------https://blog.csdn.net/lisong694767315/article/details/4554345
在图论中,拓扑排序是一个有向无环图(DAG)的所有顶点的线性序列,且该序列必须满足下面两个条件:
1.每一个‘顶点出现且只出现一次
2.若存在一条从顶点A到顶点B的路径那么在序列中顶点A出现在顶点B的前面
有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。
那么一个DAG图,如何写出它的拓扑排序呢?常用方法就是:
1.从DAG图中选择一个没有前驱的(即入读为0)的顶点并输入
2.从图中删除该顶点和所有以它为起点的有向边
3.重复1和2直到当前的DAG图为空或者当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
通常一个有向无环图可以有一个或多个拓扑排序序列。
拓扑排序通常用来“排序”具有依赖关系的任务
比如如果用一个DAG图来表示一个工程,其中每个点表示工程中的一个任务,用有向边<A,b>表示做任务B之前必须先完成任务A。故在这个工程中,任意两个任务要么具有确定的先后关系,要么是没有关系,绝对不存在互相矛盾的关系(即环路)。
拓扑排序的实现
根据上面讲的方法,我们关键是要维护一个入度为0的顶点的集合
这里我们采用邻接表来存储图
*****/
#include<iostream>
#include<list>
#include<queue>
using namespace std;
/***********************类声明********************************/
class Graph
{
int V;//顶点个数
list<int> *adj;//邻接表
queue<int> q;//维护一个入度为0的顶点的集合
int *indegree;//记录每个顶点二点入度
public:
Graph(int V);//构造函数
~Graph();//析构函数
void addEdge(int v,int w);//添加边
bool topological_sort();//拓扑排序
};
/******************************类定义******************************/
Graph::Graph(int V)
{
this->V =V;
adj = new list<int>[V];//初始化邻接表
indegree = new int[V];//入度全部初始化为0
for(int i = 0;i < V;++i)
indegree[i] = 0;
}
Graph::~Graph()
{
delate [] adj;//删除邻接表中元素
delate [] indegree;//删除入读列表愿随
}
void Graph::topological_sort()
{
for(int i = 0;i < V;++i)
if(indegree[i] == 0)//把当前入度为0的图元素加入到列表当中
q.push(i)//将所有入度为0的顶点入队
int count = 0;//计数,记录当前已经输出的顶点数
while(!q.empty())
{
int v = q.front();//从队列中取出一个顶点(这里的队列是已经从邻接表的图所表示的
//列表并已经判断没有入度的元素V
q.pop();
cout << v <<" ";//输出该顶点
++count;
//将所有V指向的顶点的入读减去1,并将入度为0的顶点入栈
list<int>:: iterator beg = adj[v].begin();
for(; beg! = adj[v].end();++beg)
if(!(--indegree[*beg]))
q.push(*beg);//若入度为0,则入栈
}
if(count < V)
return false;
else
return true;
}
**由于输出的每个顶点的同时还要删除以它为起点的边,故上述拓扑排序的时间复杂度为O(V+E).**
加下来要讲的是拓扑排序采用深度优先搜索(DFS)的思想来实现
图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中所有的顶点一次且仅访问一次。注意到树是一种特殊的图,所以树的遍历实际上也可以看作一种特殊的图的遍历。图的遍历主要有两种算法:广度优先搜索和深度优先搜索。
一。深度优先搜索DFS的基本思想
深度优先搜索算法所遵循的策略是尽可能深的搜索一个图。它的基本思想是:首先访问图中某一起始点v,然后由v出发,访问与v邻近且未被访问的任一顶点w1,再访问与w1邻近且未被访问的任一顶点w2,。。。重复上述过程。当不能再继续向下访问时,依次退回到最近被访问的顶点,若它还有邻近的顶点未被访问过,则从该点开始上述搜索过程,直到图中所有顶点均被访问过为止。
二。广度优先搜索(BFS)的算法思想
广度优先搜索类似于二叉树的层序遍历,它的基本思想:首先访问起始顶点v,接着由v出发,依次访问v的各个未访问过的邻接顶点w1,w2,。。。wi,然后再依次访问w1,w2,w3…wid的所有未被访问过的邻接顶点;再从这些访问过的顶点出发,再访问他们所有未被访问过的邻接顶点,依次类推。
广度优先搜索时一种分层的查找过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有往回退的情况,因此它不是一个递归的过程。为了实现逐层的访问,算法必须借助一个辅助队列,以记录正在访问的顶点的下一层顶点。
算法实现
1.BFS
与树相比,图的不同之处在于它存在回路/环,因此在遍历时一个顶点可能被访问多次,为了防止出现这种情况出现,我们使用一个访问标记数组visited[]来标记顶点是否已经被访问过。
/*******************************************************************************
>File name: BFS.cpp
>Author: huangenda
>E-mail:
>Created Time:
>Personal Blog
*********************************************************************************/
#include<iostream>
#include<list>
using namespace std;
/*邻接表存储有向图*/
class Graph
{
int V;//顶点的数量
list<int> *adj;//邻接表
void BFSUtil(int v,bool visited[]);//判断节点元素是否已经被访问
public:
Graph(int V);//构造函数
void addEdge(int v,int w);//向图中添加一条边
void BFS(int v);//BFS遍历
};
/*********构造函数***********/
Graph::Graph(int V)
{
this->V = V;
adj = new list<int>[V];//初始化V条链表,也就是邻接表
}
/******从顶点v出发广度优先搜索***/
void Graph::BFSUtil(int v,boolvisited[])
{
//BFS辅助队列
list<int> queue;
//将当前顶点标记为已访问并压入队列
visited[v] = true;
queue.push_back(v);
list<int>::iterator i;
while(!queue.empty())
{
//出栈
v = queue.front();
cout << v << " “;
quieue.pop_front();
//检查已出队的顶点s的所有邻接顶点
//若存在未访问的邻接点,访问它并压入队列
for(i = adj[v].begin();i!adj[v].end();i++)
{
if(!visited[*i]
{
visited[*i] = true;
queue.push_back(*i);
}
}
}
}
/**广度优先搜索**/
void Graph::BFS(int v)
{
//初始化访问的标记数组
bool *visited = new bool[V];
for(int i = 0;i < V;++i)
visited[i] = false;
//假设从指定顶点可以到达图中的所有顶点
BFSUtil(v,visited);
}
对于BFS,如果不能到达所有顶点,即存在多个连通分量时,我们就要对每个联通分量都进行一次广度优先搜索
bool visited[MAX_VERTEXT_NUM);//访问标记数组
void BFS(Graph G)
{
for(i = 0;i < G.vexnum;i++)
visited[i] = false;//初始化
for(i = 0;i < G.vexnum;i++)//从0号顶点开始遍历
if(!visited[i])//对每一个联通分量调用一次BFS
BFS(G,i);//Vi未访问过,从Vi开始BFS
呃呃呃,有点多了,改天继续看看
连接:https://blog.csdn.net/lisonglisonglisong/article/details/36704573