数据结构实训——图的基本操作

声明:  以下是我们学校在学习数据结构时进行的实训,如涉及侵权马上删除文章

      声明:本文主要用作技术分享,所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险,并遵循相关法律法规。

实训五   图的基本操作

一、实训类型

   验证性实训

二、实训目的与任务

1.掌握图的逻辑结构;

2. 掌握图的邻接矩阵存储结构;

3. 掌握图在邻接矩阵存储结构上遍历算法的实现。

4. 掌握图的邻接表存储结构;

5. 掌握图的邻接表存储结构下遍历算法的实现。

三、实训基本原理

1. 邻接矩阵

用一维数组存储图中顶点的信息,用一个二维数组表示图中各顶点之间的邻接关系信息,这个二维数组称为邻接矩阵。设图G=(V,E)有n个确定的顶点,即V={v0,v1,…,vn-1},则表示G中各顶点相邻关系为一个n×n的矩阵,矩阵的元素为:

2. 邻接表

对图G中的每个顶点vi,将所有邻接于vi的顶点vj链成一个单链表,这个单链表就称为顶点vi的邻接表。

再将所有点的邻接表表头放到数组中,就构成了图的邻接表。其中,单链表中的结点称为表结点,每个单链表设的一个头结点称为顶点结点。

3. 深度优先遍历

⑴ 访问某个顶点v

⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;

⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到

⑷ 若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

4. 广度优先遍历

⑴ 访问某一顶点v

⑵ 依次访问v的各个未被访问的邻接点v1,v2,……,vk

⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。

(4)若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

5.普利姆算法

初始化:设网图G=(V,E) ,V顶点集,E带权边集。

初始化集合U和T,U存放最小生成树中的顶点,T保存最小生成树中的边。U的初值为U={u0}(设构造最小生成树从顶点u0出发),T的初值={ }。

开始:从所有u∈U,v∈V-U的顶点中,选取具有最小权值的边(u,v),将顶点v加入集合U中,将边(u,v)加入集合T中,如此不断重复,直到U=V时,最小生成树构造完毕,这时集合T中包含了最小生成树的所有边。

四、实训设备

1.计算机

五、实训内容

1. 建立无向图的邻接矩阵存储;对建立的无向图,进行广度优先遍历。

2. 建立无向图的邻接矩阵存储;对建立的无向图,进行深度优先遍历。

3. 建立一个有向图的邻接表存储结构;对建立的有向图,进行深度优先遍历;

4. 建立一个有向图的邻接表存储结构;对建立的有向图,进行广度优先遍历。

5. 验证普利姆算法。

6. 求无向连通图的生成树(设计实训选作)

1. 2. 建立无向图的邻接矩阵存储;对建立的无向图,进行广度优先遍历,深度优先遍历。

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

#include <queue>

#define MAX   100                     /*图中最大顶点个数*/

typedef struct

    int n,e;                                    /*顶点数,边数*/

    char vexs[MAX];                     /*顶点数组*/

    int edges[MAX][MAX];            /*边的邻接矩阵*/

}MGraph;

// 初始化访问标记数组为未访问

bool visited[MAX] = {false};

// 广度优先遍历辅助函数

void BFS(MGraph G, int v) {

    std::queue<int> q;

    visited[v] = true;

    printf("%c ", G.vexs[v]);

    q.push(v);

    while (!q.empty()) {

        int u = q.front();

        q.pop();

        for (int i = 0; i < G.n; i++) {

            if (G.edges[u][i] == 1 &&!visited[i]) {

                visited[i] = true;

                printf("%c ", G.vexs[i]);

                q.push(i);

            }

        }

    }

}

// 广度优先遍历

void BFSTraverse(MGraph G) {

    for (int i = 0; i < G.n; i++) {

        visited[i] = false;

    }

    for (int i = 0; i < G.n; i++) {

        if (!visited[i]) {

            BFS(G, i);

        }

    }

}

// 深度优先遍历辅助函数

void DFS(MGraph G, int v) {

    visited[v] = true;

    printf("%c ", G.vexs[v]);

    for (int i = 0; i < G.n; i++) {

        if (G.edges[v][i] == 1 &&!visited[i]) {

            DFS(G, i);

        }

    }

}

// 深度优先遍历

void DFSTraverse(MGraph G) {

    for (int i = 0; i < G.n; i++) {

        visited[i] = false;

    }

    for (int i = 0; i < G.n; i++) {

        if (!visited[i]) {

            DFS(G, i);

        }

    }

}

void  CreateMGraph(MGraph *G)

    int i,j,k;

    char  ch1,ch2;

    printf("请输入顶点数:");

    scanf("%d",&G->n);

    printf("请输入边数:");

    scanf("%d",&G->e);

    printf("请输入各顶点信息(每个顶点以回车作为结束):\n");

    for(i=0;i<G->n;i++)

    { 

        getchar();  

        printf("输入第%d个顶点:",i+1);

        scanf("%c",&(G->vexs[i]));

    }

    for(i=0;i<G->n;i++)                

       for(j=0;j<G->n;j++)

           G->edges[i][j]=0;          /*将邻接矩阵元素全部置0*/        

    for(k=0;k<G->e;k++)

    { 

        getchar();

        printf("建立第%d条边(输入格式:顶点1,顶点2):",k+1);

        scanf("%c,%c",&ch1,&ch2);       

        for(i=0;i<G->n;i++)

          for(j=0;j<G->n;j++)

            if((ch1==G->vexs[i] && ch2==G->vexs[j]) || (ch2==G->vexs[i] && ch1==G->vexs[j]))

            { 

                G->edges[i][j]=1;

                G->edges[j][i]=1;

            }

    } 

}

void  DispMGraph(MGraph G)       

    int i,j;

    printf("\n图的邻接矩阵:\n");

    for(i=0;i<G.n;i++)

    { 

        for(j=0;j<G.n;j++)

            printf("%5d",G.edges[i][j]);

        printf("\n");

    }

}

int   main()

{  

    MGraph G;

    CreateMGraph(&G);

    printf("图的邻接矩阵:\n");

    DispMGraph(G);

    printf("广度优先遍历结果: ");

    BFSTraverse(G);

    printf("\n");

    printf("深度优先遍历结果: ");

    DFSTraverse(G);

    printf("\n");

    return 0;

}

3,4建立一个有向图的邻接表存储结构;对建立的有向图,进行深度优先遍历和广度优先遍历。

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

#include <queue>

#define MAX   100                     /*图中最大顶点个数*/

typedef struct

    int n,e;                                    /*顶点数,边数*/

    char vexs[MAX];                     /*顶点数组*/

    int edges[MAX][MAX];            /*边的邻接矩阵*/

}MGraph;

// 初始化访问标记数组

bool visited[MAX] = {false};

// 深度优先遍历辅助函数

void DFS(MGraph G, int v) {

    visited[v] = true;

    printf("%c ", G.vexs[v]);

    for (int i = 0; i < G.n; i++) {

        if (G.edges[v][i] == 1 &&!visited[i]) {

            DFS(G, i);

        }

    }

}

// 深度优先遍历

void DFSTraverse(MGraph G) {

    for (int i = 0; i < G.n; i++) {

        visited[i] = false;

    }

    for (int i = 0; i < G.n; i++) {

        if (!visited[i]) {

            DFS(G, i);

        }

    }

}

// 广度优先遍历辅助函数

void BFS(MGraph G, int start) {

    std::queue<int> q;

    visited[start] = true;

    q.push(start);

    while (!q.empty()) {

        int v = q.front();

        q.pop();

        printf("%c ", G.vexs[v]);

        for (int i = 0; i < G.n; i++) {

            if (G.edges[v][i] == 1 &&!visited[i]) {

                visited[i] = true;

                q.push(i);

            }

        }

    }

}

// 广度优先遍历

void BFSTraverse(MGraph G) {

    for (int i = 0; i < G.n; i++) {

        visited[i] = false;

    }

    for (int i = 0; i < G.n; i++) {

        if (!visited[i]) {

            BFS(G, i);

        }

    }

}

void  CreateMGraph(MGraph *G)

{   /*图的邻接矩阵建立函数*/

    int i,j,k;

    char  ch1,ch2;

    printf("请输入顶点数:");

    scanf("%d",&G->n);

    printf("请输入边数:");

    scanf("%d",&G->e);

    printf("请输入各顶点信息(每个顶点以回车作为结束):\n");

    for(i=0;i<G->n;i++)

    {   getchar();  

        printf("输入第%d个顶点:",i+1);

        scanf("%c",&(G->vexs[i]));

    }

    for(i=0;i<G->n;i++)                

       for(j=0;j<G->n;j++)

           G->edges[i][j]=0;          /*将邻接矩阵元素全部置0*/        

    for(k=0;k<G->e;k++)

    {   getchar();

        printf("建立第%d条边(输入格式:顶点1,顶点2):",k+1);

        scanf("%c,%c",&ch1,&ch2);       

        for(i=0;i<G->n;i++)

          for(j=0;j<G->n;j++)

            if(ch1==G->vexs[i]&&ch2==G->vexs[j])

            {  G->edges[i][j]=1;

            }

    } 

}

void  DispMGraph(MGraph G)       

{  /*图的邻接矩阵输出函数*/

   int i,j;

   printf("\n图的邻接矩阵:\n");

   for(i=0;i<G.n;i++)

   {   for(j=0;j<G.n;j++)

          printf("%5d",G.edges[i][j]);

       printf("\n");

   }

}

int   main()

{  

    MGraph G;

    CreateMGraph(&G);

    printf("图的邻接矩阵:\n");

    DispMGraph(G);

    printf("深度优先遍历结果: ");

    DFSTraverse(G);

    printf("\n");

    printf("广度优先遍历结果: ");

    BFSTraverse(G);

    printf("\n");

    return 0;

}

5. 验证普利姆算法。

#include <stdio.h>

#define MAX 100

#define M 32767  /*图中最大顶点个数, 32767 代表无穷大*/

// 创建无向图的邻接矩阵,并返回顶点数

int Creatcost(int cost[][MAX])

{

    int vnum, anum, i, j, k, v1, v2, w;

    printf("\n请输入顶点数和边数:");

    scanf("%d%d", &vnum, &anum);

    for (i = 0; i < vnum; i++)

    {

        for (j = 0; j < vnum; j++)

        {

            cost[i][j] = M;

        }

    }

    printf("请输入各边及权值(顶点序号从 1 开始):\n");

    for (k = 0; k < anum; k++)

    {

        printf("v1 v2 w :");

        scanf("%d%d%d", &v1, &v2, &w);

        cost[v1 - 1][v2 - 1] = w;

        cost[v2 - 1][v1 - 1] = w;

    }

    return vnum;

}

// 普里姆算法计算最小生成树

void Prim(int c[MAX][MAX], int n)

{

    int i, j, k, min, lowcost[MAX], closest[MAX];

    for (i = 1; i < n; i++) /*从顶点 0 开始*/

    {

        lowcost[i] = c[0][i];

        closest[i] = 0;

    }

    closest[0] = -1;

    for (i = 1; i < n; i++)

    {

        min = M;

        k = 1;

        for (j = 0; j < n; j++)

        {

            if (lowcost[j] < min && closest[j]!= -1)

            {

                min = lowcost[j];

                k = j;

            }

        }

        printf("(%d, %d) 权值 %d\n", closest[k], k, lowcost[k]); /*打印生成树该边及其权值*/

        closest[k] = -1;

        for (j = 1; j < n; j++)

        {

            if (closest[j]!= -1 && c[k][j] < lowcost[j])

            {

                lowcost[j] = c[k][j];

                closest[j] = k;

            }

        }

    }

}

int main()

{

    int n;

    int cost[MAX][MAX];

    n = Creatcost(cost);

    printf("\n最小生成树为:\n");

    Prim(cost, n);

    return 0;

}

6. 求无向连通图的生成树(设计实训选作)

#include <stdio.h>

#include <stdbool.h>

#include <limits.h>

#define V 5  // 顶点数量

// 找到距离生成树集合最近的顶点

int minKey(int key[], bool mstSet[]) {

    int min = INT_MAX, min_index;

    for (int v = 0; v < V; v++)

        if (mstSet[v] == false && key[v] < min)

            min = key[v], min_index = v;

    return min_index;

}

// 打印生成树

void printMST(int parent[], int graph[V][V]) {

    printf("Edge   Weight\n");

    for (int i = 1; i < V; i++)

        printf("%d - %d    %d \n", parent[i], i, graph[i][parent[i]]);

}

// 普里姆算法

void primMST(int graph[V][V]) {

    int parent[V];  // 存储生成树的父节点

    int key[V];     // 用于选择最小权重边的键值

    bool mstSet[V];  // 标记顶点是否在生成树中

    // 初始化

    for (int i = 0; i < V; i++)

        key[i] = INT_MAX, mstSet[i] = false;

    key[0] = 0;  // 从第一个顶点开始

    parent[0] = -1;  // 第一个顶点没有父节点

    for (int count = 0; count < V - 1; count++) {

        int u = minKey(key, mstSet);

        mstSet[u] = true;

        for (int v = 0; v < V; v++)

            if (graph[u][v] && mstSet[v] == false && graph[u][v] < key[v])

                parent[v] = u, key[v] = graph[u][v];

    }

    printMST(parent, graph);

}

// 测试案例

int main() {

    int graph[V][V] = {

        {0, 2, 0, 6, 0},

        {2, 0, 3, 8, 5},

        {0, 3, 0, 0, 7},

        {6, 8, 0, 0, 9},

        {0, 5, 7, 9, 0}

    };

    printf("使用普里姆算法构建的生成树:\n");

    primMST(graph);

    return 0;

}

六、实训注意事项

1.题目自选,至少要完成两个题目。

2.若完成题目个数多或设计的算法效率高,予以加分。

七、预习与思考题

1.图的邻接矩阵、邻接表的表示方法。

2.图的深度优先遍历算法、图的广度优先遍历算法。

八、实训报告要求

1.书写算法或完整程序。

2.若调试程序时程序出错,请分析出错原因及修改方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值