求连通图的深度优先生成树和广度优先生成树

本文深入探讨了图的深度优先生成树和广度优先生成树的构建方法,通过实例演示了如何使用邻接表表示图,并实现了从指定顶点出发的DFS和BFS算法来寻找生成树。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

深度优先遍历就是先根遍历,用到辅助栈;广度优先遍历就是层次遍历,用到辅助队列。

一、树(自由树)、无序树和有根树

自由树就是一个无回路的连通图(没有确定根)(在自由树中选定一顶点做根,则成为一棵通常的树)。

从根开始,为每个顶点(在树中通常称作结点)的孩子规定从左到右的次序,则它就成为一棵有序树。

在图的应用中,常常需要求给定图的一个子图,使该子图是一棵树。

二、生成树

1、生成树

如果连通图G的一个子图是一棵包含G的所有顶点的树,则该子图称为G的生成树。生成树是连通图的包含图中的所有顶点的极小连通子图。图的生成树不惟一。从不同的顶点出发进行遍历,可以得到不同的生成树。

2、深度优先生成树和广度优先生成树

生成树的求解方法:

设图G=(V,E)是一个具有n个顶点的连通图。则从G的任一顶点(源点)出发,作一次深度优先搜索(广度优先搜索),搜索到的n个顶点和搜索过程中从一个已访问过的顶点vi搜索到一个未曾访问过的邻接点vj,所经过的边(vi,vj)(共n-1条)组成的极小连通子图就是生成树。(源点是生成树的根)

通常,由深度优先搜索得到的生成树称为深度优先生成树,简称为DFS生成树;由广度优先搜索得到的生成树称为广度优先生成树,简称为BFS生成树。

①图的广度优先生成树的高度不会超过该图其它生成树的高度。

②图的生成树不惟一,从不同的顶点出发进行遍历,可以得到不同的生成树。

3、生成树的通用定义

若从图的某顶点出发,可系统地访问到图中所有顶点,则遍历时经过的边和图的所有顶点所构成的子图,称作该图的生成树。(此定义适用于无向图和有向图)

(1)若G是强连通的有向图,则从其中任一顶点v出发,都可以访问遍G中的所有顶点,从而得到以v为根的生成树。

(2)若G是有根的有向图,设根为v,则从根v出发可以完成对G的遍历,得到G的以v为根的生成树。

(3)若G是非连通的无向图,则要若干次从外部调用DFS(或BFS)算法,才能完成对G的遍历。每一次外部调用,只能访问到G的一个连通分量的顶点集,这些顶点和遍历时所经过的边构成了该连通分量的一棵DFS(或BPS)生成树。G的各个连通分量的DFS(或BFS)生成树组成了G的DFS(或BFS)生成森林。

(4)若G是非强连通的有向图,且源点又不是有向图的根,则遍历时一般也只能得到该有向图的生成森林。

/**
*    实验题目:
*        求连通图的深度优先生成树和广度优先生成树
*    实验目的:
*        领会图的深度优先遍历算法、广度优先遍历算法和生成树的概念
*    实验内容:
*        编写程序,输出一个连通图的深度优先生成树和广度优先生成树
*/

#include <stdio.h>
#include <malloc.h>

#define INF     32767               //定义∞
#define MAXV    100                 //最大顶点个数

typedef char InfoType;
/*-------------------------以下定义邻接矩阵类型---------------------------*/
typedef struct
{
    int no;                         //顶点编号
    InfoType info;                  //顶点信息
}VertexType;                        //顶点类型

typedef struct
{
    int edges[MAXV][MAXV];          //邻接矩阵数组(用一个二维数组存放顶点间关系(边或弧)的数据)
    int n;                          //顶点数
    int e;                          //边数
    VertexType vexs[MAXV];          //存放顶点信息(用一个一维数组存放图中所有顶点数据)
}MatGraph;                          //完整的图邻接矩阵类型

//邻接表表示法-将每个顶点的邻接点串成一个单链表
/*-----------以下定义邻接表类型--------------*/
typedef struct ArcNode
{
    int adjvex;                     //该边的邻接点编号
    struct ArcNode *nextarc;        //指向下一条边的指针
    int weight;                     //该边的相关信息,如权值(用整型表示)
}ArcNode;                           //边结点类型

typedef struct VNode
{
    InfoType info;                  //顶点其他信息
    int cnt;                        //存放顶点入度,仅用于拓扑排序
    ArcNode *firstarc;              //指向第一条边
}VNode;                             //邻接表结点类型

typedef struct
{
    VNode adjlist[MAXV];            //邻接表头结点数组
    int n;                          //图中顶点数
    int e;                          //图中边数
}AdjGraph;                          //完整的图邻接表类型

/*-------------------------邻接矩阵的基本运算算法---------------------------*/
/*------------由边数组A、顶点数n和边数e创建图的邻接矩阵g--------------------*/
void CreateMat(MatGraph &g, int A[MAXV][MAXV], int n, int e)
{
    int i, j;

    g.n = n;
    g.e = e;
    for(i = 0; i < g.n; i++)
        for(j = 0; j < g.n; j++)
            g.edges[i][j] = A[i][j];
}

/*------------输出邻接矩阵g--------------------*/
void DispMat(MatGraph g)
{
    int i, j;

    for(i = 0; i < g.n; i++)
    {
        for(j = 0; j < g.n; j++)
        {
            if(g.edges[i][j] != INF)
                printf("%4d", g.edges[i][j]);
            else
                printf("%4s", "∞");
        }
        printf("\n");
    }
}

/*-------------------------邻接表的基本运算算法---------------------------*/
/*-------------------由边数组A、顶点数n和边数e创建图的邻接表G--------------------*/
void CreateAdj(AdjGraph *&G, int A[MAXV][MAXV], int n, int e)
{
    int i, j;
    ArcNode *p;

    G = (AdjGraph *)malloc(sizeof(AdjGraph));
    for(i = 0; i < n; i++)                              //给邻接表中所有头结点的指针域置初值NULL
    {
        G->adjlist[i].firstarc = NULL;
    }

    for(i = 0; i < n; i++)                              //检查邻接矩阵中的每个元素
    {
        for(j = n - 1; j >= 0; j--)
        {
            if(A[i][j] != 0 && A[i][j] != INF)          //存在一条边
            {
                p = (ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点p
                p->adjvex = j;                          //邻接点编号
                p->weight = A[i][j];                    //边的权重
                p->nextarc = G->adjlist[i].firstarc;    //采用头插法插入结点p
                G->adjlist[i].firstarc = p;
            }
        }
    }
    G->n = n;
    G->e = e;
}

/*-------------------输出邻接表G--------------------*/
void DispAdj(AdjGraph *G)
{
    ArcNode *p;

    for(int i = 0; i < G->n; i++)
    {
        p = G->adjlist[i].firstarc;
        printf("%d: ", i);
        while(p != NULL)
        {
            printf("%3d[%d]->", p->adjvex, p->weight);  //邻接点编号[权重]
            p = p->nextarc;
        }
        printf("∧\n");
    }
}

/*-------------------销毁图的邻接表G--------------------*/
void DestroyAdj(AdjGraph *&G)
{
    ArcNode *pre, *p;

    for(int i = 0; i < G->n; i++)
    {
        pre = G->adjlist[i].firstarc;                   //pre指向第i个单链表的首结点
        if(pre != NULL)
        {
            p = pre->nextarc;
            while(p != NULL)                            //释放第i个单链表的所有边结点
            {
                free(pre);
                pre = p;
                p = p->nextarc;
            }
            free(pre);
        }
    }
    free(G);                                            //释放头结点数组
}

#define MAX_SIZE    100

int visited[MAXV] = {0};                             //全局数组

/*--------------求图G从顶点v出发的深度优先生成树----------------*/
void DFSTree(AdjGraph *G, int v)
{
    ArcNode *p;

    visited[v] = 1;                                         //置已访问标记
    p = G->adjlist[v].firstarc;                             //p指向顶点v的第一个相邻点
    while(p != NULL)
    {
        if(visited[p->adjvex] == 0)                         //若p->adjvex顶点未访问,递归访问它
        {
            printf("(%d,%d) ", v, p->adjvex);
            DFSTree(G, p->adjvex);
        }
        p = p->nextarc;                                     //p指向顶点v的下一个相邻点
    }
}

/*--------------求图G从顶点v出发的广度优先生成树----------------*/
void BFSTree(AdjGraph *G, int v)
{
    int que[MAXV];                                           //定义环形队列
    int que_front = 0, que_rear = 0;
    ArcNode *p;
    int visited[MAXV];                                       //定义顶点访问标志数组
    int i;
    int adjvex;

    for(i = 0; i < G->n; i++)
        visited[i] = 0;                                      //顶点访问标志数组初始化
    visited[v] = 1;                                          //置已访问标记
    que_rear++;                                              //顶点v进队
    que[que_rear] = v;
    while(que_front != que_rear)                             //队不空循环
    {
        que_front = (que_front + 1) % MAXV;
        adjvex = que[que_front];                             //出队一个顶点adjvex
        p = G->adjlist[adjvex].firstarc;                     //p指向adjvex的第一个相邻点
        while(p != NULL)                                     //查找adjvex的所有相邻点
        {
            if(visited[p->adjvex] == 0)                      //若当前邻接点未被访问
            {
                printf("(%d,%d) ", adjvex, p->adjvex);
                visited[p->adjvex] = 1;                      //置已访问标记
                que_rear = (que_rear + 1) % MAXV;
                que[que_rear] = p->adjvex;                   //顶点p->adjvex入队
            }
            p = p->nextarc;                                  //p指向顶点v的下一个邻接点
        }
    }
    printf("\n");
}

int main(void)
{
    AdjGraph *G;
    int n = 11;                                  //图顶点数
    int e = 13;                                  //图边数
    int A[MAXV][MAXV];

    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            A[i][j] = 0;

    A[0][1] = 1;
    A[0][2] = 1;
    A[0][3] = 1;

    A[1][0] = 1;
    A[1][4] = 1;
    A[1][5] = 1;

    A[2][0] = 1;
    A[2][3] = 1;
    A[2][5] = 1;
    A[2][6] = 1;

    A[3][0] = 1;
    A[3][2] = 1;
    A[3][7] = 1;

    A[4][1] = 1;

    A[5][1] = 1;
    A[5][2] = 1;

    A[6][2] = 1;
    A[6][7] = 1;
    A[6][8] = 1;
    A[6][9] = 1;

    A[7][3] = 1;
    A[7][6] = 1;
    A[7][10] = 1;

    A[8][6] = 1;
    A[9][6] = 1;
    A[10][7] = 1;

    CreateAdj(G, A, n, e);
    printf("图G的邻接表:\n");
    DispAdj(G);
    int v = 3;
    printf("深度优先生成树:\n");
    DFSTree(G, v);
    printf("\n");
    printf("广度优先生成树:\n");
    BFSTree(G, v);

    printf("销毁图的邻接表\n");
    DestroyAdj(G);

    return 0;
}

输出结果:

图G的邻接表:
0:   1[1]->  2[1]->  3[1]->∧
1:   0[1]->  4[1]->  5[1]->∧
2:   0[1]->  3[1]->  5[1]->  6[1]->∧
3:   0[1]->  2[1]->  7[1]->∧
4:   1[1]->∧
5:   1[1]->  2[1]->∧
6:   2[1]->  7[1]->  8[1]->  9[1]->∧
7:   3[1]->  6[1]-> 10[1]->∧
8:   6[1]->∧
9:   6[1]->∧
10:   7[1]->∧
深度优先生成树:
(3,0) (0,1) (1,4) (1,5) (5,2) (2,6) (6,7) (7,10) (6,8) (6,9)
广度优先生成树:
(3,0) (3,2) (3,7) (0,1) (2,5) (2,6) (7,10) (1,4) (6,8) (6,9)
销毁图的邻接表

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值