步骤:

强化讲义 · 原始转写 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照

## 步骤:

1. 先进行拓扑排序,得到拓扑序列;

2. 根据拓扑序列,正向计算每个顶点的最早发生时间 ve;

3. 根据拓扑序列反过来逆向计算每个顶点的最迟发生时间 vl;

4. 依次判断每条边 u→v:若某活动的最早时间 ve(u) 等于最迟时间 vl(v)-w(u,v),则为关键活动。 城村

## 伪代码:

仅供参考,重在理解过程,代码考察概率不大,万一考察也只是考察部分步骤(如已知拓扑序列求关键路径)。

<!-- suspect-code -->
> [!warning] 这段代码需要核对(原文第 218 页)
> OCR 体检未通过:花括号不平衡。扫描件的两栏排版(代码 + 右侧注释)有时会被交错读取,产生看起来合理但语义错误的代码。背之前请对照原文。
void CriticalPath(ALGraph G)
{
    int indegree[MAXV] = {0};
    int ve[MAXV] = {0};
    int vl[MAXV];
    int topo[MAXV];
    int front = 0, rear = 0;
    // 第一步:统计入度
// 存储每个顶点的入度// 各顶点最早开始时间// 各顶点最迟开始时间/存储拓扑排序序列时间复杂度:O(n+e) <!-- page 219 -->
for (int i = 0; i &lt; G.numVertices; i++)
ArcNode *p = G.VerticesList[i].first;
while (p != NULL)
{
    p = p-&gt;next; indegree[p-&gt;adjvex]++; // 考矿
}
}
// 第二步:拓扑排序,并计算每个顶点的最早发生时间 ve
for (int i = 0; i &lt; G.numVertices; i++)
if (indegree[i] == 0)
topo[rear++] = i;
{
    int u = topo[front++];
    ArcNode *p = G.VerticesList[u].first;
    while (p != NULL)
    {X
        int v = p-&gt;adjvex;
        if (--indegree[v] == 0)
        topo[rear++] = v;
        // 更新 ve 值
        if (ve[u] + p-&gt;weight &gt; ve[v])
        ve[v] = ve[u] + p-&gt;weight;
        p = p-&gt;next;
        15
    }
    // 如果 rear &lt; G.numVertices,图中有环,不存在关键路径
    if (rear &lt; G.numVertices)
    {
        输出:图中存在环
    } return;
    // 第三步:初始化 vl 数组
    for (int i = 0; i &lt; G.numVertices; i++)
    vl[i] = ve[G.numVertices - 1]; //默认项目总工期
    // 第四步:逆拓扑序更新 vl 值
    for (int i = G.numVertices - 1; i &gt;= 0; i--)
    {
        int u = topo[i];
        ArcNode *p = G.VerticesList[u].first;
        while (p != NULL)
        {
            int v = p-&gt;adjvex;
            if (vl[v] - p-&gt;weight &lt; vl[u])
            p = p-&gt;next;
        }
    }
<!-- page 220 --> <!-- code-audit: false_positive --> > [!info] 已人工审读:这是第 218–220 页 CriticalPath 的跨页结尾,花括号与函数开头跨块配对;其中输出动作本来就是伪代码占位。
// 第五步:输出关键活动
for (int u = 0; u &lt; G.numVertices; u++)
{
    ArcNode *p = G.VerticesList[u].first;
    while (p != NULL)
    {
        int v = p-&gt;adjvex;
        int e = ve[u];
        int l = vl[v] - p-&gt;weight;
        if (e == l) // 输出活动&lt;u,v&gt;是关键活动
        p = p-&gt;next;
    }
}
}
<!-- page 221 --> ## 5.4 章节练习 ## 5.4.1 习题 【习题 5-1】给定一个用邻接表保存的有向图G,设计算法 int OddInDegree(ALGraph G),返回出度为奇数的顶点个数。邻接表定义如下: <!-- verified-by-hand --> > [!success] 已结合原文第 221 页及 Markdown 上下文人工校对 > ArcNode 的左花括号及 nextarc 字段丢失,typedef 行混入水印。
typedef struct ArcNode
{
    int adjvex;
    struct ArcNode *nextarc;
} ArcNode;
typedef struct
{
    int data;
    ArcNode *firstarc;
} VNode;
typedef struct
{
    VNode VerticesList[MAXV];
    int numVertices, arcnum;
} ALGraph;
(3)说明所设计算法的时间复杂度。 (1)给出算法的基本设计思想。(2) 采用C或 C++ 语言描述算法,关键之处给出注释。 <!-- page 222 --> 【习题 5-2】给定一个用邻接表保存的无向图G,设计算法 int IsConnected(ALGraph G)判断该图是否连通,若连通则返回1,否则返回0。邻接表定义如下:
typedef struct ArcNode {
    int adjvex; // 该弧所指向的顶点
    struct ArcNode *nextarc; // 指向下一条弧的指针
}ArcNode; //边结点
typedef struct
{
    int data; // 顶点信息
    ArcNode *firstarc; // 指向第一条依附该顶点的弧的指针
}VNode;  // 顶点结点
typedef struct
{
    VNode VerticesList[MAXV]; // 顶点数组
    int numVerticesarcnum; //图的当前顶点数和弧数
}ALGraph;
(3) 说明所设计算法的时间复杂度。 (1) 给出算法的基本设计思想。(2) 采用 C 或 C++ 语言描述算法,关键之处给出注释。 <!-- page 223 --> 【习题 5-3】给定一个用邻接表保存的无向图 G,设计算法 int HasCycle(ALGraph \*G) 判断该图是否含有环,若含有环则返回1,否则返回0。邻接表定义如下:
typedef struct ArcNode
{
    int adjvex; // 该弧所指向的顶点
    struct ArcNode *nextarc; // 指向下一条弧的指针
}ArcNode; //边结点
typedef struct
{
    ArcNode *VerticesList[MAXV]; // 顶点数组
    int numVertices, arcnum; //图的当前顶点数和弧数
}ALGraph;
(1) 给出算法的基本设计思想。(2) 采用C或C++ 语言描述算法,关键之处给出注释。 (3) 说明所设计算法的时间复杂度。 <!-- page 224 --> 【习题5-4】给定一个用邻接表保存的带权无向图G,你可以直接调用该函数ArcNode\*FindEdge()得到一个指针,指向当前未处理过的边中权值最小的那条边(内部实现细节不需要考虑)。设计算法int MSTWeight(ALGraph G)判断返回最小生成树的权值之和。邻接表定义如下: 城村 <!-- verified-by-hand --> > [!success] 已结合原文第 224 页及 Markdown 上下文人工校对 > VNode 声明混入水印,ALGraph 的左花括号丢失。
typedef struct ArcNode
{
    int ivex, jvex;
    struct ArcNode *nextarc;
    int weight;
} ArcNode;
typedef struct
{
    int data;
    ArcNode *firstarc;
} VNode;
typedef struct
{
    VNode VerticesList[MAXV];
    int numVertices, arcnum;
} ALGraph;
(1)FindEdge()函数每次会从某个数据结构中取出最小边,应该使用什么数据结构?为什么? (2)给出算法的基本设计思想。(3)采用C或C++ 语言描述算法,关键之处给出注释。 <!-- page 225 --> 【习题5-5】设无向图G=(V,E),如果顶点可被分割为两个互不相交的子集,并且图中每条边的两个顶点i和j分别属于这两个不同的顶点集,则称图G为一个二分图。无向图G为二分图的充要条件是:G中至少包含两个顶点且G中任意一个简单回路所包含的边数为偶数。图G采用邻接矩阵存储,类型定义如下: 贼村
typedef struct {
    int numV, numE; // 顶点数和边数
    char VerticesList[MAXV]; // 顶点信息
    int Edge[MAXV] [MAXV]; //权值矩阵
}MGraph;
请设计算法 int IsBipartite (MGraph G),判断无向图 G 是否为二分图,是则返回 1,否则返回0。(1)给出算法的基本设计思想。(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。 <!-- page 226 --> 【习题 5-6】请设计算法:int ponents(ALGraph G),对给定的无向图 G,返回其中连通分量的个数。邻接表定义如下: <!-- verified-by-hand --> > [!success] 已结合原文第 226 页及 Markdown 上下文人工校对 > 两个结构体左花括号丢失,类型声明混入中文,numVertices 与 arcnum 粘连。
typedef struct ArcNode
{
    int adjvex;
    struct ArcNode *nextarc;
} ArcNode;
typedef struct
{
    int data;
    ArcNode *firstarc;
} VNode;
typedef struct
{
    VNode VerticesList[MAXV];
    int numVertices, arcnum;
} ALGraph;
(3) 说明所设计算法的时间复杂度。 (1) 给出算法的基本设计思想。(2) 采用 C或 C++ 语言描述算法,关键之处给出注释。 <!-- page 227 -->
原资料配图
原资料配图 · 转写内容请回源核验

## 【习题5-7】已知无向图G 类型定义如下:

<!-- verified-by-hand -->
> [!success] 已结合原文第 227 页及 Markdown 上下文人工校对
> VexBox 与 AMLGraph 的左花括号丢失,顶点数与边数变量粘连。
typedef struct EBox
{
    int mark;
    int ivex, jvex;
    struct EBox *ilink, *jlink;
} EBox;
typedef struct VexBox
{
    char data;
    EBox *firstEdge;
} VexBox;
typedef struct
{
    VexBox adjMulList[MAXV];
    int numVertices, numEdges;
} AMLGraph;
请设计算法:int KDegree(AMLGraph G, int k),对给定的无向图 G,返回图 G 中顶点下标为k的顶点的度。例如图G2,k=1时,对应顶点 V2,度为3,返回3。要求:(1)请问图的这种存储结构叫什么? (2)给出算法的基本设计思想。(3)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。 <!-- page 228 --> 【习题5-8】有向完全图的定义是:在有向图中,任意两个顶点u和ν之间,必须同时存在u->v 和 v->u 两条弧。对于有向图 G,请设计算法:int IsComplete(OLGraph G) 判断它是不是有向完全图,如果是则返回1,否则返回0。请回答:(1)应该采用以下哪种数据结构保存:邻接多重表还是十字链表? (2)写出该数据结构定义。(3)采用C 或C++ 语言描述算法,关键之处给出注释。 X <!-- page 229 --> ## 5.4.2 真题 【2021年41 题】 (15 分)已知无向连通图G 由顶点集V和边集E组成,|E|> 0,当 G 中度为奇数的顶点个数为不大于2的偶数时,G 存在包含所有边且长度为|E|的路径(称为EL路径)。设图 G 采用邻接矩阵存储,类型定义如下:
typedef struct{
    int numVertices, numEdges; //图中实际的顶点数和边数
    char VerticesList[MAXV]; //顶点表。MAXV为已定义常量
    int Edge[MAXV] [MAXV]; //邻接矩阵
}MGraph;
请设计算法 int IsExistEL(MGraph G),判断 G 是否存在 EL 路径,若存在,则返回 1,否则返回0。要求:(1)给出算法的基本设计思想。(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。(3)说明你所设计算法的时间复杂度和空间复杂度。 <!-- page 230 --> 【2023年41题】(13分)已知有向图G采用邻接矩阵存储,类型定义如下: <!-- verified-by-hand --> > [!success] 已结合原文第 230 页及 Markdown 上下文人工校对 > 邻接矩阵图结构体左花括号丢失。
typedef struct
{
    int numVertices, numEdges;
    char VerticesList[MAXV];
    int Edge[MAXV][MAXV];
} MGraph;
将图中出度大于入度的顶点称为 K顶点。例如在下图中,顶点a和b为K顶点。
原资料配图
原资料配图 · 转写内容请回源核验

G,输出G中所有的Κ顶点,并返回Κ顶点的个数。要求:1)给出算法的基本设计思想。

2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。

<!-- page 231 -->

【2024年41题】(13分)2023年10月26日,神舟十七号载人飞船发射取得圆满成功再次彰显了中国航天事业辉煌成就。载人航天工程是包含众多子工程的复杂系统工程,为了保证工程的有序开展,需要明确各子工程的前导子工程,以协调各子工程的实施。该问题可以简化、抽象为有向图的拓扑序列问题。已知有向图G采用邻接矩阵存储,类型定义如下。

typedef struct //图的类型定义
{
    int numVertices,numEdges; //图的顶点数和有向边数
    char VerticesList[MAXV]; //顶点表,MAXV为已定义常量
    int Edge[MAXV] [MAXV]; //邻接矩阵
}MGraph;
请设计算法:int uniquely(MGragh G),判定 G 是否存在唯一的拓扑序列,若是,则返回 1,否则返回0。要求如下。1)给出算法的基本设计思想。(4分)2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。(9分) <!-- page 232 --> ## 5.5 章节答案 ## 5.5.1 习题 ## 习题5-1 给定一个用邻接表保存的有向图 G,设计算法 int OddInDegree(ALGraph G),返回出度为奇数的顶点个数。邻接表定义如下: <!-- verified-by-hand --> > [!success] 已结合原文第 232 页及 Markdown 上下文人工校对 > VNode 的左花括号丢失。
typedef struct ArcNode
{
    int adjvex;
    struct ArcNode *nextarc;
} ArcNode;
typedef struct
{
    int data;
    ArcNode *firstarc;
} VNode;
typedef struct
{
    VNode VerticesList[MAXV];
    int numVertices, arcnum;
} ALGraph;
(1)给出算法的基本设计思想。(2)采用C或C++ 语言描述算法,关键之处给出注释。 (3) 说明所设计算法的时间复杂度。 ## 【答案】(1) 1. 设置出度数组 outdegree,初始为 0。 2. 遍历所有顶点和边,统计每个顶点的出度。 3. 扫描每个顶点,统计出度为奇数的顶点数。(2)
int OddInDegree(ALGraph G)
{
    //1.设置出度数组
    int outdegree[MAXV] = {0};
    //2.遍历所有顶点和边,统计每个顶点的出度
    for (int i = 0; i &lt; G.numVertices; i++)
    {
        //遍历当前顶点i发出的所有边
        ArcNode *p = G.VerticesList[i].firstarc;
        while (p != NULL)
        {
            outdegree[i]++;
            p = p-&gt;nextarc; // 处理下一条边
        }
    }
    //3.扫描每个顶点,统计出度的顶点数
    int count = 0;
    for (int i = 0; i &lt; G.numVertices; i++)
    if (outdegree[i] % 2 = 1) count++;
    return count;
}
<!-- page 233 -->
(3) O(n+e)
## 习题5-2 给定一个用邻接表保存的无向图 G,设计算法 int IsConnected(ALGraph G) 判断该图是否连通,若连通则返回1,否则返回0。邻接表定义如下: X
typedef struct ArcNode
{
    int adjvex; // 该弧所指向的顶点
    struct ArcNode *nextarc; //指向下一条弧的指针
}ArcNode; //边结点
typedef struct
{
    int data; //顶点信息
    ArcNode *firstarc; // 指向第一条依附该顶点的弧的指针
}VNode; // 顶点结点
typedef struct
{
    VNode VerticesList[MAXV];// 顶点数组
    int numVertices, arcnum; //图的当前顶点数和弧数
}ALGraph;
(1) 给出算法的基本设计思想。(2) 采用 C或 C++ 语言描述算法,关键之处给出注释。 (3) 说明所设计算法的时间复杂度。 【解析】判断图是否连通只需要从任意结点出发遍历即可,遍历完成后如果存在结点未访问则 说明该图不连通,否则该图连通。遍历可以选择广度优先遍历BFS 或深度优先遍历DFS 中的 一种,本方法用BFS 完成。【答案】(1)入 1. 队列q保存待访问结点,将0号元素入队。 2. 如果队中还有元素则循环:选择队头元素u出队访问并记录,然后将u相邻的未入队过的顶点依次入队,循环执行直到队列访问完,BFS完成。 <!-- page 234 --> 3.扫描每个顶点是否都已被访问,如果存在顶点未被访问,则说明不连通,返回0;否则连通,返回1。