习题5-5
原始讲义 · 非增强校订版 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照
## 习题5-5
设无向图G=(V,E),如果顶点可被分割为两个互不相交的子集,并且图中每条边的两个顶点i和i分别属于这两个不同的顶点集,则称图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++语言描述算法,关键之处给出注释。
## 【解析】
本题是要判断是否存在含有偶数个元素的环,此处可以用DFS也可以用BFS。
## 【答案】
(1)使用广度优先遍历BFS算法:从任意未染色的顶点开始,先将起始0号顶点染色为1,并将其入队。依次处理队列中的顶点,出队一个顶点v,遍历其所有相邻顶点u。若u未被染色,则将其染色为与ν不同的颜色(1 或2),并将其入队。若u已被染色且颜色与ν相同,则说明图G不是二分图,返回0。重复上述步骤,直到队列为空。若所有顶点都被染色且没有相邻顶点颜色相同的情况,则说明图G是二分图,返回1。
(2) 代码如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 169 页及 Markdown 上下文人工校对
> 邻接扫描缺花括号、return 跑到函数外且混入水印;权值矩阵应以非零判边。
int IsBipartite(MGraph G)
{
int color[MAXV] = {0}, q[MAXV];
for (int s = 0; s < G.numV; s++)
{
if (color[s] != 0) continue;
int front = 0, rear = 0;
color[s] = 1;
q[rear++] = s;
while (front < rear)
{
int u = q[front++];
for (int w = 0; w < G.numV; w++)
{
if (G.Edge[u][w] == 0) continue;
if (color[w] == color[u]) return 0;
if (color[w] == 0)
{
color[w] = 3 - color[u];
q[rear++] = w;
}
}
}
}
return 1;
}
<!-- page 170 -->
## 习题5-6
请设计算法:int ponents(ALGraph G),对给定的无向图G,返回其中连通分量的个数。邻接表定义如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 170 页及 Markdown 上下文人工校对
> 本页邻接表定义的 ArcNode 与 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】DFS
## (1)
1. 设置计数器count,初始为0。准备一个 visited数组,记录哪些点走过了,初始都为 false。
2. 循环检查每个顶点:如果该顶点还未访问过,说明找到了一个新的连通分量的起点,就把count加1,递归调用深度优先遍历 DFS。 A
(2) 代码如下:
bool visited[MAXV] = {false};
// DFS函数,用于遍历一个连通分量
void DFS(ALGraph G, int v)
{
visited[v] = true;
ArcNode *p = G.VerticesList[v].firstarc;
while (p != NULL)
{
if (visited[p->adjvex] != true)
DFS(G, p->adjvex);
p = p->nextarc;
}
}
int ponents(ALGraph G)
{
int count = 0;
for (int i = 0; i < G.numVertices; i++)
if (visited[i] != true)
{
count++;
DFS(G, i);
}
return count;
}
<!-- page 171 -->
## 【答案2】并查集
(1)
1. 设置并查集 father,初始时每个结点的 father[i] =i 自身。
2. 遍历所有边<u,v>,合并u和 ν所在的并查集。
3. 统计这些顶点属于几个集合。
<!-- verified-by-hand -->
> [!success] 已结合原文第 171 页及 Markdown 上下文人工校对
> union 与主函数均错嵌在 find;图类型错误,统计与 return 又错嵌进遍历循环。
int father[MAXV];
int find(int x)
{
if (father[x] != x) father[x] = find(father[x]);
return father[x];
}
void unite(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx != fy) father[fy] = fx;
}
int ponents(ALGraph G)
{
for (int i = 0; i < G.numVertices; i++) father[i] = i;
for (int i = 0; i < G.numVertices; i++)
for (ArcNode *p = G.VerticesList[i].firstarc; p; p = p->nextarc)
unite(i, p->adjvex);
int count = 0;
for (int i = 0; i < G.numVertices; i++) if (find(i) == i) count++;
return count;
}
<!-- page 172 -->
## 习题5-7
已知无向图G 类型定义如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 172 页及 Markdown 上下文人工校对
> EBox、VexBox、AMLGraph 均缺少左花括号并混入 OCR 水印。
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;
<!-- page 173 -->
原资料配图 · 转写内容请回源核验
请设计算法:int KDegree(AMLGraph G, int k),对给定的无向图 G,返回图 G 中顶点下标为 k 的顶点的度。例如图 G2,k=1时,对应顶点V2,度为3,返回3。要求:
(1)请问图的这种存储结构叫什么?
(2) 给出算法的基本设计思想。
(3)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
## 【解析】
(1) 邻接多重表
(2)
1. 从 G.adjMulList[k].firstEdge 开始,拿到第一条边。
2. 循环执行,直到查找完链表(NULL):
a. 计数器加一。
b. 根据当前顶点是ivex还是jvex,选择正确的下一条边指针。
(3)
<!-- verified-by-hand -->
> [!success] 已结合原文第 173 页及 Markdown 上下文人工校对
> return 被错嵌进 while,后续十字链表类型定义也被串进同一函数。
int KDegree(AMLGraph G, int k)
{
int degree = 0;
EBox *p = G.adjMulList[k].firstEdge;
while (p != NULL)
{
degree++;
p = p->ivex == k ? p->ilink : p->jlink;
}
return degree;
}
<!-- page 174 -->
## 习题5-8
有向完全图的定义是:在有向图中,任意两个顶点u和ν之间,必须同时存在u->v和v->u两条弧。对于有向图G,请设计算法:int IsComplete(OLGraph G)判断它是不是有向完全图,如果是则返回1,否则返回0。请回答:
(1)应该采用以下哪种数据结构保存:邻接多重表还是十字链表?
(2) 写出该数据结构定义。
(3)采用C或C++语言描述算法,关键之处给出注释。
## 【解析】
对于简单图来说,有向图是有向完全图等价于:e=n×(n-1)。或者可以遍历统计所有顶点,看每个顶点是否都可以到达其他顶点。
## 【答案】
(1)十字链表
(2) 定义:
## (3) 代码如下:
int IsComplete(OLGraph G)
{
if (G.numEdges == G.numVertices*(G.numVertices-1))
return 1; // e = n×(n-1)
else 0;
}
或者统计所有结点的出度是否为 n-1:
<!-- verified-by-hand -->
> [!success] 已结合原文第 174 页及 Markdown 上下文人工校对
> 十字链表边类型被写成 ArcNode,numVertices 大小写错误且尾部混入 OCR 水印。
int IsComplete(OLGraph G)
{
for (int i = 0; i < G.numVertices; i++)
{
int count = 0;
for (ArcBox *p = G.VerticesList[i].firstout; p; p = p->tlink) count++;
if (count != G.numVertices - 1) return 0;
}
return 1;
}
<!-- page 175 -->
## 5.5.2 真题
## 2021年41题
(15 分)已知无向连通图G 由顶点集V和边集E 组成,E>0,当G 中度为奇数的顶点个数为不大于2的偶数时,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)说明你所设计算法的时间复杂度和空间复杂度。
分析可知本题需要访问所有顶点,统计所有顶点的度,然后判断有多少个度为奇数的顶点,只需要对邻接矩阵一次遍历即可实现。 n
(1)访问所有的顶点,两层循环判断每两个顶点间是否有边相连、统计所有顶点的度,进而求出有多少个度为奇数的顶点,对邻接矩阵一次遍历即可。
(2) 算法如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 175 页及 Markdown 上下文人工校对
> 逻辑或被 OCR 识别为 |l;权值邻接矩阵应以非零判边。
int IsExistEL(MGraph G)
{
int count = 0;
for (int i = 0; i < G.numVertices; i++)
{
int degree = 0;
for (int j = 0; j < G.numVertices; j++)
if (G.Edge[i][j] != 0) degree++;
if (degree % 2 == 1) count++;
}
return count == 0 || count == 2;
}
<!-- page 176 -->
(3) 时间复杂度:O(n²)空间复杂度:O(1)
## 2023 年41题
(13分)已知有向图G采用邻接矩阵存储,类型定义如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 176 页及 Markdown 上下文人工校对
> MGraph 结构体缺少左花括号。
typedef struct
{
int numVertices, numEdges;
char VerticesList[MAXV];
int Edge[MAXV][MAXV];
} MGraph;
将图中出度大于入度的顶点称为K顶点。例如在下图中,顶点a和b为K顶点。
原资料配图 · 转写内容请回源核验
请设计算法:int printVertices(MGraph G),对给定的任意非空有向图G,输出 G 中所有的 K 顶点,并返回 K 顶点的个数。要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
## 【解析】
出度大于入度的顶点称为K顶点,本题关键是遍历图求出所有顶点的入度和出度,才能判断出顶点是否为K顶点。依次判断所有顶点是否为K顶点,并统计数量即可。
注:以下两种答案本质一样,仅供参考
## 【答案】
(1)图的邻接矩阵G.Edge[i][j]>0表示i到j有边,二重循环遍历邻接矩阵,算出每个顶点的入读indegree和出度outdegree,然后判断每个顶点是否为 K顶点,如果是则输出并且计数器count+1,最后返回count即可。
(2) 写法 1:
int printVertices(MGraph G)
{
int indegree, outdegree; // 入度和出度
int count = 0; // 计数器count初值为0
for (int i = 0; i < G.numVertices; i++)
{
indegree = outdegree = 0; //更换i之后要清零,重新统计入度和出度
for (int j = 0; j < G.numvertices; j++)
if (G.Edge[i][j] > 0) outdegree++; // i到j有边,i的出度+1
for (int j = 0; j < G.numvertices; j++)
if (G.Edge[j][i] > 0) indegree++; // j到i有边,i的入度+1
if (outdegree > indegree) // 如果i的出度大于入度则说明i是K顶点
{
printf("%c", G.VerticesList[i]);
count++;
}
}
return count;
}
<!-- page 177 -->
## 写法2:
<!-- verified-by-hand -->
> [!success] 已结合原文第 177 页及 Markdown 上下文人工校对
> return 错嵌进顶点循环,中文输出缺注释,numVertices 大小写错误。
int printVertices(MGraph G)
{
int count = 0;
for (int i = 0; i < G.numVertices; i++)
{
int indegree = 0, outdegree = 0;
for (int j = 0; j < G.numVertices; j++)
{
if (G.Edge[i][j] > 0) outdegree++;
if (G.Edge[j][i] > 0) indegree++;
}
if (outdegree > indegree)
{
printf("%c", G.VerticesList[i]);
count++;
}
}
return count;
}
## 2024年41题
(13分)2023年10月26日,神舟十七号载人飞船发射取得圆满成功,再次彰显了中国航天事业辉煌成就。载人航天工程是包含众多子工程的复杂系统工程,为了保证工程的有序开展,需要明确各子工程的前导子工程,以协调各子工程的实施。该问题可以简化、抽象为有向图的拓扑序列问题。已知有向图G采用邻接矩阵存储,类型定义如下。
<!-- page 178 -->
<!-- verified-by-hand -->
> [!success] 已结合原文第 178 页及 Markdown 上下文人工校对
> MGraph 结构体缺少左花括号且 MAXV 大小写错误。
typedef struct
{
int numVertices, numEdges;
char VerticesList[MAXV];
int Edge[MAXV][MAXV];
} MGraph;
请设计算法:int uniquely(MGraqh G),判定 G是否存在唯一的拓扑序列,若是,则返回1,否则返回0。要求如下。
1)给出算法的基本设计思想。(4分)
2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。(9分)
【解析1】
原资料配图 · 转写内容请回源核验