习题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 &lt; G.numV; s++)
    {
        if (color[s] != 0) continue;
        int front = 0, rear = 0;
        color[s] = 1;
        q[rear++] = s;
        while (front &lt; rear)
        {
            int u = q[front++];
            for (int w = 0; w &lt; 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-&gt;adjvex] != true)
        DFS(G, p-&gt;adjvex);
        p = p-&gt;nextarc;
    }
}
int ponents(ALGraph G)
{
    int count = 0;
    for (int i = 0; i &lt; G.numVertices; i++)
    if (visited[i] != true)
    {
        count++;
        DFS(G, i);
    }
    return count;
}
<!-- page 171 -->
(3) O(n+e)。
## 【答案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 &lt; G.numVertices; i++) father[i] = i;
    for (int i = 0; i &lt; G.numVertices; i++)
        for (ArcNode *p = G.VerticesList[i].firstarc; p; p = p-&gt;nextarc)
            unite(i, p-&gt;adjvex);
    int count = 0;
    for (int i = 0; i &lt; 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-&gt;ivex == k ? p-&gt;ilink : p-&gt;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 &lt; G.numVertices; i++)
    {
        int count = 0;
        for (ArcBox *p = G.VerticesList[i].firstout; p; p = p-&gt;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 &lt; G.numVertices; i++)
    {
        int degree = 0;
        for (int j = 0; j &lt; 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 &lt; G.numVertices; i++)
    {
        indegree = outdegree = 0; //更换i之后要清零,重新统计入度和出度
        for (int j = 0; j &lt; G.numvertices; j++)
        if (G.Edge[i][j] &gt; 0) outdegree++; // i到j有边,i的出度+1
        for (int j = 0; j &lt; G.numvertices; j++)
        if (G.Edge[j][i] &gt; 0) indegree++; // j到i有边,i的入度+1
        if (outdegree &gt; indegree) // 如果i的出度大于入度则说明i是K顶点
        {
            printf(&quot;%c&quot;, 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 &lt; G.numVertices; i++)
    {
        int indegree = 0, outdegree = 0;
        for (int j = 0; j &lt; G.numVertices; j++)
        {
            if (G.Edge[i][j] &gt; 0) outdegree++;
            if (G.Edge[j][i] &gt; 0) indegree++;
        }
        if (outdegree &gt; indegree)
        {
            printf(&quot;%c&quot;, 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】
原资料配图
原资料配图 · 转写内容请回源核验