C语言 拓扑排序算法解析

0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在理解 拓扑排序的思想并用源代码加以实现;

0.2) 图论算法基础知识

【1】图论若干相关定义

1.1)图G定义:一个图G=(V,E)由顶点及集V 和 边集E组成, 每一条边就是一个点对(v, w);

1.2)边邻接:当且仅当(v,w)∈E, 在无向图中, w和v邻接,且v也和w邻接;(还有第3中成分:边的权值)

1.3)路径: 一条路径是一个顶点序列 w1, w2,…,wn, 使得(wi,wi+1)∈E,这样一条路径长是该路径上的边数;

1.4)简单路径:其上的所有顶点都是互异的,但第一个顶点和最后一个顶点可能相同;

1.5)连通图(对于无向图而言): 如果在一个无向图中从每一个顶点到每个其他顶点都存在一条路径,则称该无向图是连通的;

1.6)强连通图(对于有向图而言):具有无向图连通性质的有向图称为是强连通的;

1.7)基础图:去掉有向图上的方向所形成的图;

1.8)弱连通图:如果有向图不是强连通的, 但基础图是连通的, 那么该图称为是弱连通的;

1.9)完全图: 是指每一对顶点间都存在一条边的图;

【2】图的表示

2.1)邻接矩阵:对于每条边(u, v), 我们设置 A[u][v]=1,否则数组的元素为0;如果边是有权的,设置A[u][v] 等于该权值
且用一个很大或者很小的权作为标记表示不存在的边;(∞)

2.1.1)邻接表的空间需求是 Θ(|V|^2);如果图是稠密的:|E| = Θ(|V|^2) ,
则邻接矩阵是合适的表示方法;如果在大部分应用中,图都是稀疏的;

2.2)邻接表(图的标准表示方法):如果图是稀疏的, 则使用邻接表来表示。对每一个顶点,我们使用一个表存放所有邻接的顶点。此时的空间需求为 O(|E| +
|V|);

2.2.1)引入散列表:在应用中,顶点都是名字而不是数字, 这些名字在编译时是未知的。由于我们不能够通过未知名字为一个数组做索引,
因此我们必须提供从名字到数字的映射。完成这项工作最容易的 方法是使用散列表, 在该散列表中我们对每个顶点存储一个名字以及一个范围在1到 |V|
之间的内部编号;

2.2.2)邻接表的一个荔枝:



【1】拓扑排序(有向无圈图才有资格谈拓扑排序,有向且要无圈)

1.1)拓扑排序定义: 拓扑排序是对有向无圈图的顶点的一种排序, 它使得如果存在一条从 vi 到 vj的路径,那么在排序中 vj出现在
vi的后面;(有向无圈图:一个有向图没有圈, 圈:满足w1=wn 且长度至少为1个一条路径)

1.2)拓扑排序应用(有点像联机算法): 课程选修顺序, 如有向边(v, w)表明课程v 必须在课程w 选修前修完;(显然,如果图含有圈,
那么拓扑排序是不可能的)


1.3)一个简单的求拓扑排序的算法是: 先找出任意一个没有入边的顶点。 然后我们显示出该顶点,并将它和他的边一起从图中删除;

1.4)改进后的求拓扑排序的算法:

1.4.1)算法描述:通过将所有入度为0 的顶点放在一个特殊的盒子中而避免这种无效的劳动。此时 FindNewVertexOfIndegreeZero
函数返回(并删除)盒子中的 任一顶点。当我们降低这些邻接顶点的入度时,检查每一个顶点并在它的入度 降为0 时把它放入盒子中;

1.4.2)实现盒子(引入了队列):为实现这个盒子,我们使用一个栈或者队列;首先,
对每一个顶点计算它的入度。然后,将所有入度为0的顶点放入一个初始为空的队列中。当队列不空时,删除一个顶点v, 并将与v
邻接的所有顶点的入度减1。只要一个顶点的入度降为0, 就把该顶点放入队列中。此时,拓扑排序就是顶点出队的顺序;

1.4.3)我们的假设:我们假设图已经被读到一个邻接表中且入度已计算并被放入一个数组内;

1.4.4)时间复杂度是: O(|E| + |V|);


【2】拓扑排序源代码 + printing results:



2.1)download source code:

https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/p219_topSort

2.2)source code at a glance(for full code, please download source code
following the given link above):

#include "adjTable.h"

#include "queue.h"

void topSort(AdjTable* adj, int size, int* indegreeArray, Queue queue)

{

int i;

int counter;

ElementType vertex;

ElementType adjVertex;

AdjTable temp;

AdjTable temp1;

for(i=0; i

if(!indegreeArray[i]) // find the element who has zero indegree in adjoining
table

enQueue(queue, i); //let the element enter the queue

printf("\t topSorting sequence is as follows: ");

counter = 0;

while(!isEmpty(queue))

{

vertex = deQueue(queue); // if the queue is empty, conducting departing
queue

temp = adj[vertex]->next;

while(temp)

{

adjVertex = temp->index; // each adjVertex adjacent to vertex

if(--indegreeArray[adjVertex] == 0)

enQueue(queue, adjVertex);

temp1 = temp->next;

free(temp);

temp = temp1;

}

printf("vertex[%d] ", vertex+1);

counter++;

}

if(counter != size)

Error("failed top sorting, for graph has a cycle, from func topSort !");

disposeQueue(queue);

printf("\n\t");

}

// initializing indegree array with given size

int *initIndegree(int size)

{

int *indegreeArray;

int i;

indegreeArray = (int*)malloc(sizeof(int) * size);

if(!indegreeArray)

{

Error("failed intialization ,for out of space ,from func initIndegree");

return NULL;

}

for(i=0; i < size; i++)

indegreeArray[i] = 0;

return indegreeArray;

}

int main()

{

AdjTable* adj;

int *indegreeArray;

Queue queue;

int size = 7;

int i;

int j;

int column = 3;

int adjTable[7][3] =

{

{2, 4, 3},

{4, 5, 0},

{6, 0, 0},

{6, 7, 3},

{4, 7, 0},

{0, 0, 0},

{6, 0, 0}

};

printf("\n\n\t ====== test for topological sorting with adjoining table
======\n");

adj = initAdjTable(size);

indegreeArray = initIndegree(size);

queue = initQueue(size);

printf("\n\n\t ====== the initial adjoining table is as
follows:======\n");

for(i = 0; i < size; i++)

for(j = 0; j < column; j++)

if(adjTable[i][j])

{

insertAdj(adj, adjTable[i][j]-1, i); // insertAdj the adjoining table
over

indegreeArray[adjTable[i][j]-1]++; // update indegree of elements

}

printAdjTable(adj, size);

// topSorting starts

//void topSort(AdjTable* adj, int size, int* indegreeArray, Queue queue)

topSort(adj, size, indegreeArray, queue);

return 0;

}

2.3)printing results:

时间: 2024-10-10 02:47:15

C语言 拓扑排序算法解析的相关文章

C语言实现排序算法之归并排序详解_C 语言

排序算法中的归并排序(Merge Sort)是利用"归并"技术来进行排序.归并是指将若干个已排序的子文件合并成一个有序的文件. 一.实现原理: 1.算法基本思路 设两个有序的子文件(相当于输入堆)放在同一向量中相邻的位置上:R[low..m],R[m+1..high],先将它们合并到一个局部的暂存向量R1(相当于输出堆)中,待合并完成后将R1复制回R[low..high]中. (1)合并过程 合并过程中,设置i,j和p三个指针,其初值分别指向这三个记录区的起始位置.合并时依次比较R[i

C语言 奇偶排序算法详解及实例代码_C 语言

C语言奇偶排序算法 奇偶排序,或奇偶换位排序,或砖排序,是一种相对简单的排序算法,最初发明用于有本地互连的并行计算.这是与冒泡排序特点类似的一种比较排序.该算法中,通过比较数组中相邻的(奇-偶)位置数字对,如果该奇偶对是错误的顺序(第一个大于第二个),则交换.下一步重复该操作,但针对所有的(偶-奇)位置数字对.如此交替进行下去. 使用奇偶排序法对一列随机数字进行排序的过程 处理器数组的排序 在并行计算排序中,每个处理器对应处理一个值,并仅有与左右邻居的本地互连.所有处理器可同时与邻居进行比较.交

C语言选择排序算法及实例代码_C 语言

选择排序是排序算法的一种,这里以从小到大排序为例进行讲解. 基本思想及举例说明 选择排序(从小到大)的基本思想是,首先,选出最小的数,放在第一个位置:然后,选出第二小的数,放在第二个位置:以此类推,直到所有的数从小到大排序. 在实现上,我们通常是先确定第i小的数所在的位置,然后,将其与第i个数进行交换. 下面,以对 3  2  4  1 进行选择排序说明排序过程,使用min_index 记录当前最小的数所在的位置. 第1轮 排序过程 (寻找第1小的数所在的位置) 3  2  4  1(最初, m

【算法小总结】拓扑排序+例题解析

题目1449:确定比赛名次 时间限制:1 秒内存限制:128 兆特殊判题:否提交:669解决:293 题目描述: 有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前.现在请你编程序确定排名. 输入: 输入有若干组,每组中的第一行为二个数N(1<=N<=500),M:其中N表示队伍

C语言 选择排序算法详解及实现代码_C 语言

选择排序是排序算法的一种,这里以从小到大排序为例进行讲解. 基本思想及举例说明 选择排序(从小到大)的基本思想是,首先,选出最小的数,放在第一个位置:然后,选出第二小的数,放在第二个位置:以此类推,直到所有的数从小到大排序. 在实现上,我们通常是先确定第i小的数所在的位置,然后,将其与第i个数进行交换. 下面,以对 3  2  4  1 进行选择排序说明排序过程,使用min_index 记录当前最小的数所在的位置. 第1轮 排序过程 (寻找第1小的数所在的位置) 3  2  4  1(最初, m

拓扑排序(一) C语言详解

拓扑排序介绍 拓扑排序(Topological Order)是指,将一个有向无环图(Directed Acyclic Graph简称DAG)进行排序进而得到一个有序的线性序列. 这样说,可能理解起来比较抽象.下面通过简单的例子进行说明! 例如,一个项目包括A.B.C.D四个子部分来完成,并且A依赖于B和D,C依赖于D.现在要制定一个计划,写出A.B.C.D的执行顺序.这时,就可以利用到拓扑排序,它就是用来确定事物发生的顺序的. 在拓扑排序中,如果存在一条从顶点A到顶点B的路径,那么在排序结果中B

数据结构和算法17 之拓扑排序

  这一节我们学习一个新的排序算法,准确的来说,应该叫"有向图的拓扑排序".所谓有向图,就是A->B,但是B不能到A.与无向图的区别是,它的边在邻接矩阵里只有一项(友情提示:如果对图这种数据结构部不太了解的话,可以先看一下这篇博文:数据结构和算法之 无向图.因为拓扑排序是基于图这种数据结构的). 有向图的邻接矩阵如下表所示:   A B C A 0 1 1 B 0 0 1 C 0 0 0         所以针对前面讨论的无向图,邻接矩阵的上下三角是对称的,有一半信息是冗余的.而

【算法导论】邻接表存储的拓扑排序

        上一篇文章中讲述了用邻接矩阵存储的图的拓扑排序,下面本文介绍用邻接表存储的图的拓扑排序.         关于拓扑排序的概念及基本思想,我在上一篇文章中已经较为详细的描述了,这里不在介绍.我们知道利用邻接矩阵进行拓扑排序时,程序实现较为简单,但是效率不高,算法的复杂度为O(n^3).而利用邻接表会使入度为0的顶点的操作简化,从而提高算法的效率. 在邻接表存储结构中,为了便于检查每个顶点的入度,可在顶点表中增加一个入度域(id),这样的邻接表如下图所示,这样只需对由n个元素构成的顶

C++实现顺序排序算法简单示例代码_C 语言

本文实例讲述了最直接的顺序排序法VC++示例代码,还记得以前上学时候这是计算机的必考题,而且在排序算法中,顺序排序似乎是最简单的了,也是最容易掌握的.现在列出来让大家重新回顾一下! 具体代码如下: //顺序排序 void InsertSort(int r[], int n){ for (int i=2; i<n; i++){ r[0]=r[i]; //设置哨兵 for (int j=i-1; r[0]<r[j]; j--) //寻找插入位置 r[j+1]=r[j]; //记录后移 r[j+1]