本系列文章为浙江大学陈越、何钦铭数据结构学习笔记,前面的系列文章链接如下:
数据结构基础:P1-基本概念
数据结构基础:P2.1-线性结构—>线性表
数据结构基础:P2.2-线性结构—>堆栈
数据结构基础:P2.3-线性结构—>队列
数据结构基础:P2.4-线性结构—>应用实例:多项式加法运算
数据结构基础:P2.5-线性结构—>应用实例:多项式乘法与加法运算-C实现
数据结构基础:P3.1-树(一)—>树与树的表示
数据结构基础:P3.2-树(一)—>二叉树及存储结构
数据结构基础:P3.3-树(一)—>二叉树的遍历
数据结构基础:P3.4-树(一)—>小白专场:树的同构-C语言实现
数据结构基础:P4.1-树(二)—>二叉搜索树
数据结构基础:P4.2-树(二)—>二叉平衡树
数据结构基础:P4.3-树(二)—>小白专场:是否同一棵二叉搜索树-C实现
数据结构基础:P4.4-树(二)—>线性结构之习题选讲:逆转链表
数据结构基础:P5.1-树(三)—>堆
数据结构基础:P5.2-树(三)—>哈夫曼树与哈夫曼编码
数据结构基础:P5.3-树(三)—>集合及运算
数据结构基础:P5.4-树(三)—>入门专场:堆中的路径
数据结构基础:P5.5-树(三)—>入门专场:File Transfer
数据结构基础:P6.1-图(一)—>什么是图
数据结构基础:P6.2-图(一)—>图的遍历
数据结构基础:P6.3-图(一)—>应用实例:拯救007
数据结构基础:P6.4-图(一)—>应用实例:六度空间
数据结构基础:P6.5-图(一)—>小白专场:如何建立图-C语言实现
数据结构基础:P7.1-图(二)—>树之习题选讲:Tree Traversals Again
数据结构基础:P7.2-图(二)—>树之习题选讲:Complete Binary Search Tree
数据结构基础:P7.3-图(二)—>树之习题选讲:Huffman Codes
数据结构基础:P7.4-图(二)—>最短路径问题
数据结构基础:P7.5-图(二)—>哈利·波特的考试
数据结构基础:P8.1-图(三)—>最小生成树问题
文章目录
一、拓扑排序
1.1 AOV网络
下面这张表告诉了我们计算机专业的各个课程之间他们的相互依赖的关系。这只有15门课而已,所以你还是可以比较容易的
把这个课程的先后顺序给理出来的。但是整个真正的计算机专他有好几十门课,当课程数量大大增多的时候,这就变成了一个相当复杂的事情,这就要个程序来解决这个问题。那么在我们数据结构里头有什么工具可以被用来解决这个问题呢?
我们可以用一个图来解决这个问题。要描述一个图,它的顶点就是每一门课,从v到w的边意味着v是w的预修课程
。
1.2 拓扑排序
拓扑排序:
例子:
1.3 拓扑排序的两种算法
简单粗暴的算法对应伪代码如下:
void TopSort()
{
for ( cnt = 0; cnt < |V|; cnt++ ) {
V = 未输出的入度为0的顶点; //直接遍历查找的话,复杂度为O(V)
if ( 这样的V不存在 ) {
Error ( “图中有回路” );
break;
}
输出V,或者记录V的输出序号;
for ( V 的每个邻接点 W )
Indegree[W]––; //入度-1,代表V已经学了,需要的预修课程少一门了
}
}
算法复杂度为: T = O ( ∣ V ∣ 2 ) \rm{T=O(|V|^2)} T=O(∣V∣2)
聪明的算法: 随时将入度变为0的顶点放到一个容器里,所以到下一次我要找一个度为0的顶点的时候,我就不用重新去扫描所有的顶点集合,我直接到这个容器里头取一个出来就好了。所以这个时间复杂度就顿时变成了一个常数级的时间复杂度。
void TopSort()
{
for ( 图中每个顶点 V )
if ( Indegree[V]==0 )
Enqueue( V, Q );
while ( !IsEmpty(Q) ) {
V = Dequeue( Q );
输出V,或者记录V的输出序号; cnt++;
for ( V 的每个邻接点 W )
if ( ––Indegree[W]==0 )
Enqueue( W, Q );
}
if ( cnt != |V| )
Error( “图中有回路” );
}
算法复杂度为: T = O ( ∣ V ∣ + ∣ E ∣ ) \rm{T=O(|V|+|E|)} T=O(∣V∣+∣E∣),此算法还可以用来检测有向图是否是有向无环图。
二、关键路径
2.1 AOE网络
AOE(Activity On Edge)网络:每一条边代表的是一道工序或者说是一个活动。这种网络一般是被用于安排一个很庞大的项目,这个庞大的项目会分成很多道工序,工序和工序之间是有先后的依赖关系的。
我们的活动是表示在边上,顶点表示的是这个活动到达这个顶点的时候就结束了。
通常在画这个网络的时候,我们把一个顶点分成三块,包含:顶点编号、最早完成时间、最晚完成时间
。同时,在边的上面写持续时间
,边的下边写机动时间
。这些东西具体的含义我们下面看个例子。
例子:
邻接表存储-拓扑排序算法
/* 邻接表存储 - 拓扑排序算法 */
bool TopSort( LGraph Graph, Vertex TopOrder[] )
{ /* 对Graph进行拓扑排序, TopOrder[]顺序存储排序后的顶点下标 */
int Indegree[MaxVertexNum], cnt;
Vertex V;
PtrToAdjVNode W;
Queue Q = CreateQueue( Graph->Nv );
/* 初始化Indegree[] */
for (V=0; V<Graph->Nv; V++)
Indegree[V] = 0;
/* 遍历图,得到Indegree[] */
for (V=0; V<Graph->Nv; V++)
for (W=Graph->G[V].FirstEdge; W; W=W->Next)
Indegree[W->AdjV]++; /* 对有向边<V, W->AdjV>累计终点的入度 */
/* 将所有入度为0的顶点入列 */
for (V=0; V<Graph->Nv; V++)
if ( Indegree[V]==0 )
AddQ(Q, V);
/* 下面进入拓扑排序 */
cnt = 0;
while( !IsEmpty(Q) ){
V = DeleteQ(Q); /* 弹出一个入度为0的顶点 */
TopOrder[cnt++] = V; /* 将之存为结果序列的下一个元素 */
/* 对V的每个邻接点W->AdjV */
for ( W=Graph->G[V].FirstEdge; W; W=W->Next )
if ( --Indegree[W->AdjV] == 0 )/* 若删除V使得W->AdjV入度为0 */
AddQ(Q, W->AdjV); /* 则该顶点入列 */
} /* while结束*/
if ( cnt != Graph->Nv )
return false; /* 说明图中有回路, 返回不成功标志 */
else
return true;
}
小测验
1、拓扑序一定是唯一的。(错误)
2、下图给定了一个项目的AOE。整个项目最早完工需要的时间是
A. 17
B. 19
C. 20
D. 23
答案:D
3、在上图中,如果<0,2>组能加快进度,整个项目就能提前完工。(正确)