出自:http://www.cnblogs.com/skywang12345/p/3711489.html
拓扑排序(一)之 C语言详解
本章介绍图的拓扑排序。和以往一样,本文会先对拓扑排序的理论知识进行介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现。
目录
1. 拓扑排序介绍
2. 拓扑排序的算法图解
3. 拓扑排序的代码说明
4. 拓扑排序的完整源码和测试程序
转载请注明出处:http://www.cnblogs.com/skywang12345/
更多内容:数据结构与算法系列 目录
拓扑排序介绍
拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列。
这样说,可能理解起来比较抽象。下面通过简单的例子进行说明!
例如,一个项目包括A、B、C、D四个子部分来完成,并且A依赖于B和D,C依赖于D。现在要制定一个计划,写出A、B、C、D的执行顺序。这时,就可以利用到拓扑排序,它就是用来确定事物发生的顺序的。
在拓扑排序中,如果存在一条从顶点A到顶点B的路径,那么在排序结果中B出现在A的后面。
拓扑排序的算法图解
拓扑排序算法的基本步骤:
1. 构造一个队列Q(queue) 和 拓扑排序的结果队列T(topological);
2. 把所有没有依赖顶点的节点放入Q;
3. 当Q还有顶点的时候,执行下面步骤:
3.1 从Q中取出一个顶点n(将n从Q中删掉),并放入T(将n加入到结果集中);
3.2 对n每一个邻接点m(n是起点,m是终点);
3.2.1 去掉边;
3.2.2 如果m没有依赖顶点,则把m放入Q;
注:顶点A没有依赖顶点,是指不存在以A为终点的边。

以上图为例,来对拓扑排序进行演示。

第1步:将B和C加入到排序结果中。
顶点B和顶点C都是没有依赖顶点,因此将C和C加入到结果集T中。假设ABCDEFG按顺序存储,因此先访问B,再访问C。访问B之后,去掉边和,并将A和D加入到队列Q中。同样的,去掉边和,并将F和G加入到Q中。
(01) 将B加入到排序结果中,然后去掉边和;此时,由于A和D没有依赖顶点,因此并将A和D加入到队列Q中。
(02) 将C加入到排序结果中,然后去掉边和;此时,由于F有依赖顶点D,G有依赖顶点A,因此不对F和G进行处理。
第2步:将A,D依次加入到排序结果中。
第1步访问之后,A,D都是没有依赖顶点的,根据存储顺序,先访问A,然后访问D。访问之后,删除顶点A和顶点D的出边。
第3步:将E,F,G依次加入到排序结果中。
因此访问顺序是:B -> C -> A -> D -> E -> F -> G
拓扑排序的代码说明
拓扑排序是对有向无向图的排序。下面以邻接表实现的有向图来对拓扑排序进行说明。
1. 基本定义
// 邻接表中表对应的链表的顶点
typedef struct _ENode
{
int ivex; // 该边所指向的顶点的位置
struct _ENode *next_edge; // 指向下一条弧的指针
}ENode, *PENode;
// 邻接表中表的顶点
typedef struct _VNode
{
char data; // 顶点信息
ENode *first_edge; // 指向第一条依附该顶点的弧
}VNode;
// 邻接表
typedef struct _LGraph
{
int vexnum; // 图的顶点的数目
int edgnum; // 图的边的数目
VNode vexs[MAX];
}LGraph;
(01) LGraph是邻接表对应的结构体。 vexnum是顶点数,edgnum是边数;vexs则是保存顶点信息的一维数组。
(02) VNode是邻接表顶点对应的结构体。 data是顶点所包含的数据,而firstedge是该顶点所包含链表的表头指针。
(03) ENode是邻接表顶点所包含的链表的节点对应的结构体。 ivex是该节点所对应的顶点在vexs中的索引,而nextedge是指向下一个节点的。
2. 拓扑排序
/*
* 拓扑排序
*
* 参数说明:
* G -- 邻接表表示的有向图
* 返回值:
* -1 -- 失败(由于内存不足等原因导致)
* 0 -- 成功排序,并输入结果
* 1 -- 失败(该有向图是有环的)
*/
int topological_sort(LGraph G)
{
int i,j;
int index = 0;
int head = 0; // 辅助队列的头
int rear = 0; // 辅助队列的尾
int *queue; // 辅组队列
int *ins; // 入度数组
char *tops; // 拓扑排序结果数组,记录每个节点的排序后的序号。
int num = G.vexnum;
ENode *node;
ins = (int *)malloc(num*sizeof(int)); // 入度数组
tops = (char *)malloc(num*sizeof(char));// 拓扑排序结果数组
queue = (int *)malloc(num*sizeof(int)); // 辅助队列
assert(ins!=NULL && tops!=NULL && queue!=NULL);
memset(ins, 0, num*sizeof(int));
memset(tops, 0, num*sizeof(char));
memset(queue, 0, num*sizeof(int));
// 统计每个顶点的入度数
for(i = 0; i < num; i++)
{
node = G.vexs[i].first_edge;
while (node != NULL)
{
ins[node->ivex]++;
node = node->next_edge;
}
}
// 将所有入度为0的顶点入队列
for(i = 0; i < num; i ++)
if(ins[i] == 0)
queue[rear++] = i; // 入队列
while (head != rear) // 队列非空
{
j = queue[head++]; // 出队列。j是顶点的序号
tops[index++] = G.vexs[j].data; // 将该顶点添加到tops中,tops是排序结果
node = G.vexs[j].first_edge; // 获取以该顶点为起点的出边队列
// 将与"node"关联的节点的入度减1;
// 若减1之后,该节点的入度为0;则将该节点添加到队列中。
while(node != NULL)
{
// 将节点(序号为node->ivex)的入度减1。
ins[node->ivex]--;
// 若节点的入度为0,则将其"入队列"
if( ins[node->ivex] == 0)
queue[rear++] = node->ivex; // 入队列
node = node->next_edge;
}
}
if(index != G.vexnum)
{
printf("Graph has a cycle\n");
free(queue);
free(ins);
free(tops);
return 1;
}
// 打印拓扑排序结果
printf("== TopSort: ");
for(i = 0; i < num; i ++)
printf("%c ", tops[i]);
printf("\n");
free(queue);
free(ins);
free(tops);
return 0;
}
说明:
(01) queue的作用就是用来存储没有依赖顶点的顶点。它与前面所说的Q相对应。
(02) tops的作用就是用来存储排序结果。它与前面所说的T相对应。
拓扑排序的完整源码和测试程序
拓扑排序源码(list_dg.c)
/**
* C: 无回路有向图(Directed Acyclic Graph)的拓扑排序
* 该DAG图是通过邻接表实现的。
*
* @author skywang
* @date 2014/04/22
*/
#include
#include
#include
#include
#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a) (sizeof(a)/sizeof(a[0]))
// 邻接表中表对应的链表的顶点
typedef
struct
_ENode
{
int
ivex
;
// 该边所指向的顶点的位置
struct
_ENode
*
next_edge
;
// 指向下一条弧的指针
}
ENode
,
*
PENode
;
// 邻接表中表的顶点
typedef
struct
_VNode
{
char
data
;
// 顶点数据
ENode
*
first_edge
;
// 指向第一条依附该顶点的弧
}
VNode
;
// 邻接表
typedef
struct
_LGraph
{
int
vexnum
;
// 图的顶点的数目
int
edgnum
;
// 图的边的数目
VNode
*
vexs
;
// 图的顶点数组
}
LGraph
;
/*
* 返回ch在matrix矩阵中的位置
*/
static
int
get_position
(
LGraph
g
,
char
ch
)
{
int
i
;
for
(
i
=
0
;
i
<
g
.
vexnum
;
i
++
)
if
(
g
.
vexs
[
i
].
data
==
ch
)
return
i
;
return
-
1
;
}
/*
* 读取一个输入字符
*/
static
char
read_char
()
{
char
ch
;
do
{
ch
=
getchar
();
}
while
(
!
isLetter
(
ch
));
return
ch
;
}
/*
* 将node链接到list的末尾
*/
static
void
link_last
(
ENode
*
list
,
ENode
*
node
)
{
ENode
*
p
=
list
;
while
(
p
->
next_edge
)
p
=
p
->
next_edge
;
p
->
next_edge
=
node
;
}
/*
* 创建邻接表对应的图(自己输入)
*/
LGraph
*
create_lgraph
()
{
char
c1
,
c2
;
int
v
,
e
;
int
i
,
p1
,
p2
;
ENode
*
node1
,
*
node2
;
LGraph
*
pG
;
// 输入"顶点数"和"边数"
printf
(
"input vertex number: "
);
scanf
(
"%d"
,
&
v
);
printf
(
"input edge number: "
);
scanf
(
"%d"
,
&
e
);
if
(
v
<
1
||
e
<
1
||
(
e
>
(
v
*
(
v
-
1
))))
{
printf
(
"input error: invalid parameters!
\n
"
);
return
NULL
;
}
pG
=
(
LGraph
*
)
malloc
(
sizeof
(
LGraph
));
assert
(
pG
!=
NULL
);
// 初始化"顶点数"和"边数"
pG
->
vexnum
=
v
;
pG
->
edgnum
=
e
;
pG
->
vexs
=
(
VNode
*
)
malloc
(
pG
->
vexnum
*
sizeof
(
VNode
));
assert
(
pG
->
vexs
!=
NULL
);
// 初始化"邻接表"的顶点
for
(
i
=
0
;
i
<
pG
->
vexnum
;
i
++
)
{
printf
(
"vertex(%d): "
,
i
);
pG
->
vexs
[
i
].
data
=
read_char
();
pG
->
vexs
[
i
].
first_edge
=
NULL
;
}
// 初始化"邻接表"的边
for
(
i
=
0
;
i
<
pG
->
edgnum
;
i
++
)
{
// 读取边的起始顶点和结束顶点
printf
(
"edge(%d): "
,
i
);
c1
=
read_char
();
c2
=
read_char
();
p1
=
get_position
(
*
pG
,
c1
);
p2
=
get_position
(
*
pG
,
c2
);
// 初始化node1
node1
=
(
ENode
*
)
malloc
(
sizeof
(
ENode
));
node1
->
ivex
=
p2
;
// 将node1链接到"p1所在链表的末尾"
if
(
pG
->
vexs
[
p1
].
first_edge
==
NULL
)
pG
->
vexs
[
p1
].
first_edge
=
node1
;
else
link_last
(
pG
->
vexs
[
p1
].
first_edge
,
node1
);
}
return
pG
;
}
/*
* 创建邻接表对应的图(用已提供的数据)
*/
LGraph
*
create_example_lgraph
()
{
char
c1
,
c2
;
char
vexs
[]
=
{
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
,
'G'
};
char
edges
[][
2
]
=
{
{
'A'
,
'G'
},
{
'B'
,
'A'
},
{
'B'
,
'D'
},
{
'C'
,
'F'
},
{
'C'
,
'G'
},
{
'D'
,
'E'
},
{
'D'
,
'F'
}};
int
vlen
=
LENGTH
(
vexs
);
int
elen
=
LENGTH
(
edges
);
int
i
,
p1
,
p2
;
ENode
*
node1
,
*
node2
;
LGraph
*
pG
;
if
((
pG
=
(
LGraph
*
)
malloc
(
sizeof
(
LGraph
)))
==
NULL
)
assert
(
pG
!=
NULL
);
memset
(
pG
,
0
,
sizeof
(
LGraph
));
// 初始化"顶点数"和"边数"
pG
->
vexnum
=
vlen
;
pG
->
edgnum
=
elen
;
pG
->
vexs
=
(
VNode
*
)
malloc
(
pG
->
vexnum
*
sizeof
(
VNode
));
assert
(
pG
->
vexs
!=
NULL
);
// 初始化"邻接表"的顶点
for
(
i
=
0
;
i
<
pG
->
vexnum
;
i
++
)
{
pG
->
vexs
[
i
].
data
=
vexs
[
i
];
pG
->
vexs
[
i
].
first_edge
=
NULL
;
}
// 初始化"邻接表"的边
for
(
i
=
0
;
i
<
pG
->
edgnum
;
i
++
)
{
// 读取边的起始顶点和结束顶点
c1
=
edges
[
i
][
0
];
c2
=
edges
[
i
][
1
];
p1
=
get_position
(
*
pG
,
c1
);
p2
=
get_position
(
*
pG
,
c2
);
// 初始化node1
node1
=
(
ENode
*
)
malloc
(
sizeof
(
ENode
));
node1
->
ivex
=
p2
;
// 将node1链接到"p1所在链表的末尾"
if
(
pG
->
vexs
[
p1
].
first_edge
==
NULL
)
pG
->
vexs
[
p1
].
first_edge
=
node1
;
else
link_last
(
pG
->
vexs
[
p1
].
first_edge
,
node1
);
}
return
pG
;
}
/*
* 深度优先搜索遍历图的递归实现
*/
static
void
DFS
(
LGraph
G
,
int
i
,
int
*
visited
)
{
int
w
;
ENode
*
node
;
visited
[
i
]
=
1
;
printf
(
"%c "
,
G
.
vexs
[
i
].
data
);
node
=
G
.
vexs
[
i
].
first_edge
;
while
(
node
!=
NULL
)
{
if
(
!
visited
[
node
->
ivex
])
DFS
(
G
,
node
->
ivex
,
visited
);
node
=
node
->
next_edge
;
}
}
/*
* 深度优先搜索遍历图
*/
void
DFS_traverse
(
LGraph
G
)
{
int
i
;
int
*
visited
;
// 顶点访问标记
visited
=
(
int
*
)
malloc
(
G
.
vexnum
*
sizeof
(
int
));
assert
(
visited
!=
NULL
);
// 初始化所有顶点都没有被访问
memset
(
visited
,
0
,
G
.
vexnum
*
sizeof
(
int
));
printf
(
"== DFS: "
);
for
(
i
=
0
;
i
<
G
.
vexnum
;
i
++
)
{
if
(
!
visited
[
i
])
DFS
(
G
,
i
,
visited
);
}
printf
(
"
\n
"
);
free
(
visited
);
}
/*
* 广度优先搜索(类似于树的层次遍历)
*/
void
BFS
(
LGraph
G
)
{
int
head
=
0
;
int
rear
=
0
;
int
*
queue
;
// 辅组队列
int
*
visited
;
// 顶点访问标记
int
i
,
j
,
k
;
ENode
*
node
;
queue
=
(
int
*
)
malloc
(
G
.
vexnum
*
sizeof
(
int
));
visited
=
(
int
*
)
malloc
(
G
.
vexnum
*
sizeof
(
int
));
assert
(
queue
!=
NULL
&&
visited
!=
NULL
);
memset
(
queue
,
0
,
G
.
vexnum
*
sizeof
(
int
));
memset
(
visited
,
0
,
G
.
vexnum
*
sizeof
(
int
));
printf
(
"== BFS: "
);
for
(
i
=
0
;
i
<
G
.
vexnum
;
i
++
)
{
if
(
!
visited
[
i
])
{
visited
[
i
]
=
1
;
printf
(
"%c "
,
G
.
vexs
[
i
].
data
);
queue
[
rear
++
]
=
i
;
// 入队列
}
while
(
head
!=
rear
)
{
j
=
queue
[
head
++
];
// 出队列
node
=
G
.
vexs
[
j
].
first_edge
;
while
(
node
!=
NULL
)
{
k
=
node
->
ivex
;
if
(
!
visited
[
k
])
{
visited
[
k
]
=
1
;
printf
(
"%c "
,
G
.
vexs
[
k
].
data
);
queue
[
rear
++
]
=
k
;
}
node
=
node
->
next_edge
;
}
}
}
printf
(
"
\n
"
);
free
(
visited
);
free
(
queue
);
}
/*
* 拓扑排序
*
* 参数说明:
* G -- 邻接表表示的有向图
* 返回值:
* -1 -- 失败(由于内存不足等原因导致)
* 0 -- 成功排序,并输入结果
* 1 -- 失败(该有向图是有环的)
*/
int
topological_sort
(
LGraph
G
)
{
int
i
,
j
;
int
index
=
0
;
int
head
=
0
;
// 辅助队列的头
int
rear
=
0
;
// 辅助队列的尾
int
*
queue
;
// 辅组队列
int
*
ins
;
// 入度数组
char
*
tops
;
// 拓扑排序结果数组,记录每个节点的排序后的序号。
int
num
=
G
.
vexnum
;
ENode
*
node
;
ins
=
(
int
*
)
malloc
(
num
*
sizeof
(
int
));
// 入度数组
tops
=
(
char
*
)
malloc
(
num
*
sizeof
(
char
));
// 拓扑排序结果数组
queue
=
(
int
*
)
malloc
(
num
*
sizeof
(
int
));
// 辅助队列
assert
(
ins
!=
NULL
&&
tops
!=
NULL
&&
queue
!=
NULL
);
memset
(
ins
,
0
,
num
*
sizeof
(
int
));
memset
(
tops
,
0
,
num
*
sizeof
(
char
));
memset
(
queue
,
0
,
num
*
sizeof
(
int
));
// 统计每个顶点的入度数
for
(
i
=
0
;
i
<
num
;
i
++
)
{
node
=
G
.
vexs
[
i
].
first_edge
;
while
(
node
!=
NULL
)
{
ins
[
node
->
ivex
]
++
;
node
=
node
->
next_edge
;
}
}
// 将所有入度为0的顶点入队列
for
(
i
=
0
;
i
<
num
;
i
++
)
if
(
ins
[
i
]
==
0
)
queue
[
rear
++
]
=
i
;
// 入队列
while
(
head
!=
rear
)
// 队列非空
{
j
=
queue
[
head
++
];
// 出队列。j是顶点的序号
tops
[
index
++
]
=
G
.
vexs
[
j
].
data
;
// 将该顶点添加到tops中,tops是排序结果
node
=
G
.
vexs
[
j
].
first_edge
;
// 获取以该顶点为起点的出边队列
// 将与"node"关联的节点的入度减1;
// 若减1之后,该节点的入度为0;则将该节点添加到队列中。
while
(
node
!=
NULL
)
{
// 将节点(序号为node->ivex)的入度减1。
ins
[
node
->
ivex
]
--
;
// 若节点的入度为0,则将其"入队列"
if
(
ins
[
node
->
ivex
]
==
0
)
queue
[
rear
++
]
=
node
->
ivex
;
// 入队列
node
=
node
->
next_edge
;
}
}
if
(
index
!=
G
.
vexnum
)
{
printf
(
"Graph has a cycle
\n
"
);
free
(
queue
);
free
(
ins
);
free
(
tops
);
return
1
;
}
// 打印拓扑排序结果
printf
(
"== TopSort: "
);
for
(
i
=
0
;
i
<
num
;
i
++
)
printf
(
"%c "
,
tops
[
i
]);
printf
(
"
\n
"
);
free
(
queue
);
free
(
ins
);
free
(
tops
);
return
0
;
}
/*
* 打印邻接表图
*/
void
print_lgraph
(
LGraph
G
)
{
int
i
;
ENode
*
node
;
printf
(
"== List Graph:
\n
"
);
for
(
i
=
0
;
i
<
G
.
vexnum
;
i
++
)
{
printf
(
"%d(%c): "
,
i
,
G
.
vexs
[
i
].
data
);
node
=
G
.
vexs
[
i
].
first_edge
;
while
(
node
!=
NULL
)
{
printf
(
"%d(%c) "
,
node
->
ivex
,
G
.
vexs
[
node
->
ivex
].
data
);
node
=
node
->
next_edge
;
}
printf
(
"
\n
"
);
}
}
void
main
()
{
LGraph
*
pG
;
// 自定义"图"(自己输入数据)
//pG = create_lgraph();
// 采用已有的"图"
pG
=
create_example_lgraph
();
// 打印图
print_lgraph
(
*
pG
);
// 打印图
//DFS_traverse(*pG); // 深度优先搜索
//BFS(*pG); // 广度优先搜索
topological_sort
(
*
pG
);
// 拓扑排序
}