C++数据结构实验三:图

目录

一、实验目的

二、问题分析及数据结构设计

三、算法设计

四、功能模块程序流程图

五、实验结果

六、算法分析

七、操作说明

八、源代码


近来有空闲,把前几个学期做的实验上传上来。如有错误的地方欢迎大佬批评指正,有更好的方法也期待您的分享~


实验内容

1.编写函数,采用邻接矩阵表示法,构造一个无向网。

2.编写函数,实现从键盘输入数据,建立一个有向图的邻接表。

3.编写函数,输出该邻接表。

4.编写函数,采用邻接表存储实现无向图的深度优先非递归遍历。

5.编写函数,采用邻接表存储实现无向图的广度优先遍历。

6.编写一个主函数,在主函数中设计一个简单的菜单,分别调试上述算法。


一、实验目的

1.熟悉理解图的基本概念、逻辑结构以及存储结构;

2.掌握编程实现图遍历具体算法:深度优先、广度优先;

3.深刻理解图的顺序存储(邻接矩阵)与链式存储(邻接链表)的特性;

4.训练在编程上控制复杂结构的能力,为今后控制更为复杂结构,进而解决有一定难度的复杂问题奠定基础。

二、问题分析及数据结构设计

本次开发任务要求我们编写一个能调试几种构造和搜索图的系统。该任务需要实现以下功能:构造无向网、建立与输出邻接表、深度优先非递归遍历无向图、广度优先遍历无向图等。

其次是数据结构设计,为了实现这些功能,我们需要构建数据模型。图的存储结构除了要存储图中各个顶点本身的信息外,同时还要存储顶点与顶点之间的所有关系(边的信息)。常用的图的存储结构主要有邻接矩阵和邻接表。

首先定义顶点最大个数 MAX_VERTEX_NUM 为100。

定义邻接矩阵中顶点数目为vexnum,边的数目为arcnum;定义一维数组vexs[MAX_VERTEX_NUM]存储顶点信息;定义二维数组arcs[MAX_VERTEX_NUM] [MAX_VERTEX_NUM]存储顶点与顶点之间的关系。

定义邻接表中顶点的数据类型为VerTexData;定义边结点结构体node中,整型目标节点dest,整型边的权值cost,下一边链接指针link;定义顶点结点结构体 VerTexNode 中,顶点数据域data,边链表头指针adj;定义邻接表结构体为 VexList[MAX_VERTEX_NUM],整型顶点和边的实际数分别为n和e。

对于每个功能,我们需要设计相应的函数,函数的输入参数和返回值根据具体的需求进行设计。例如,建立无向图的邻接矩阵的函数使用字符序列g作为输入,没有返回值;非递归深度遍历函数使用节点指针G作为输入,输出遍历的结果等。另外,在实现广度优先遍历功能的过程中,要实现重放也需要额外的存储空间,利用队列的先入先出特性,定义队列q存储顶点信息。

三、算法设计

1.采用邻接矩阵创建无向网 CreateGraph():

(1)用户输入两数字,确定该网的顶点个数G.vexnum与边数G.edgenum;

(2)for循环对顶点G.vertex[i]进行赋值;

(3)for循环输入想要添加的边的权值以及边所依附的两顶点;

(4)调用LocateVex()函数确定两顶点在图中的位置;

(5)对无向图各顶点对称赋值;

(6)for循环输出顶点及权值。

图1 无向图及对应的邻接矩阵

2.创建有向图的邻接表 CreatGraph():

(1)用户输入两数字,确定该网的顶点个数G.n与边数G.e;

(2)for循环输入顶点信息G.VexList[i].data,将G.VexList[i].adj置空,表示该点已经访问过;

(3)逐条边输入,分别输入尾tail,头head,权重weight,创建边链表头指针 p,p->dest指向头head,p->cost指向权重weight。

(4)调用ShowGraph()函数,for循环输出邻接表的结点信息,及其指向的结点、边权值。

图2 有向图及无向图的邻接表存储

3.邻接表深度优先非递归遍历 dfs():

(1)初始化栈

(2)输出起始的顶点,起始顶点改为“已访问”的标志,将起始顶点进栈

(3)重复一下的操作直至栈为空:

(4)取栈顶元素顶点,存在着未被访问的邻结点W

(5)输出顶点W,将顶点W改为“已访问”,将W进栈;

(6)否则,当前顶点退栈;

图3 深度优先搜索过程

4.无向图广度优先递归遍历 bfs():

(1)图存在回路/环,为了防止在遍历时一个顶点可能被访问多次,使用一个访问标记数组visited[]来标记顶点是否已经被访问过,若visited[]为1,则该顶点被访问过;

(2)首先访问起始顶点v,接着由v出发,依次访问v的各个未访问过的邻接顶点w1,w2,…,wi,将其入队;

(3)再依次访问w1,w2,…,wi的所有未被访问过的邻接顶点;

(4)再从这些访问过的顶点出发,再访问它们所有未被访问过的邻接顶点……依次类推,直到图中所有顶点都被访问过为止。

图4 广度优先遍历过程

5.主函数部分main():

(1)用户输入1-5的数字,使用 switch 选择器选择调用CreateGraph()、Traverse()、CreatGraph()、ShowGraph()、createMGraph()、dfs()、bfs()函数来创建无向图、输出无向图、创建邻接表、输出邻接表、实现无向图的深度优先非递归遍历和广度优先遍历。

(2)用户输入6,完成程序:return 0;

四、功能模块程序流程图

1.菜单部分:

图5 主函数菜单部分

2.采用邻接矩阵创建无向网 CreateGraph():

图6 采用邻接矩阵创建无向网流程图

3.创建有向图的邻接表 CreatGraph()

图7 创建有向图的邻接表流程图

4.邻接表深度优先非递归遍历 dfs():

图8 邻接表深度优先非递归遍历流程图

5.无向图广度优先递归遍历 bfs():

图9 无向图广度优先递归遍历流程图

五、实验结果

1.输入选项:1

图10 实验结果1

2.输入选项:2和3

图11 实验结果2

3.输入选项:4

图12 实验结果3

4.输入选项:5

图13 实验结果4

5.输入选项:6

图14 实验结果5

六、算法分析

1.深度优先非递归遍历

(1)时间复杂度O(n):创建两个队列分别用于存储当前层和下一层结点,对n个结点需遍历n次,循环内各操作时间复杂度均为O(1),故总体时间复杂度为O(n);

(2)空间复杂度O(n):两队列最多需要存储n/2个结点数据,空间复杂度O(n)。

2.广度优先遍历

(1)时间复杂度O(|V| + |E|):其中 |V| 表示图中的顶点数,|E| 表示图中的边数,遍历每个顶点和每条边的时间复杂度均为 O(1),因此总时间复杂度为 O(|V| + |E|);

(2)空间复杂度O(|V| + |E|):在遍历过程中需要使用队列来存储每个被访问的顶点,而最坏情况下,所有顶点都被访问,每个顶点都需要加入队列一次,因此队列最大长度为 |V|,同时还需要额外的空间来存储邻接表,因此总空间复杂度为 O(|V| + |E|)。

七、操作说明

1.进入图系统后,会看见如下提示:

************************* 实验三  图 *************************

*                                                                                              *

*           1:创建无向图的邻接矩阵                                            *

*           2:创建有向图的邻接表                                                *

*           3:输出有向图的邻接表                                                 *

*4:(邻接表存储)实现无向图的深度优先非递归遍历DFS遍历*

*           5:(邻接表存储)实现无向图的广度优先遍历              *

*           6:退出程序                                                                   *

*                                                                                               *

*************************************************************

☆请输入一个数字选项:                                                             

2.若想创建无向图的邻接矩阵,则输入1,然后输入顶点数、边数、点集,并逐条边输入对应的点和权值。

3.若想创建有向图的邻接表,则输入2,然后输入图的顶点数边数、点集,并逐条边输入对应的点和权值。

4.若想实现输出有向图的邻接表,则输入3。

5.若想实现采用邻接表存储实现无向图的深度优先非递归遍历,则输入4。

6.若想实现采用邻接表存储实现无向图的广度优先遍历,则输入5。

7.若想退出程序,则输入6。

八、源代码

#include<iostream>
#include<malloc.h>
#include <stack>
#include<queue>
#define MAX_VERTEX_NUM 100//顶点最大个数
#define MaxInt 36767//代表无穷 
using namespace std;

//无向图邻接矩阵
typedef struct Graph    /*定义图的邻接矩阵*/
{
    int vexnum, arcnum;        //图中顶点数目、边/弧的数目
    char vexs[MAX_VERTEX_NUM];             //一维数组:存储顶点信息
    int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];           //二维数组:存储顶点与顶点之间的关系
}graph;

//有向图邻接表
typedef char VerTexData;//顶点数据类型

typedef struct node//边节点
{
    int dest;//目标节点位置
    int cost;//边的权值
    struct node* link;//下一边链接指针
}EdgeNode_youxiang;//表结点

typedef struct VerTexNode//顶点结点
{
    VerTexData data;//顶点数据域
    EdgeNode_youxiang* adj;//边链表头指针
}VerTexNode;//头结点

typedef struct AdjGraph//图的邻接表
{
    VerTexNode VexList[MAX_VERTEX_NUM];//邻接表
    int n, e;//顶点的实际数,边的实际数
}AdjGraph;

//无向图邻接表
struct Node// 边表结点
{
    int adjves;//存储顶点的下标
    struct Node* next;//连接下一个邻点
};

typedef struct Node EdgeNode;

typedef struct VertexNode//顶点表结点
{
    int ves;//顶点的值
    EdgeNode* firstedge;//相连的顶点的值
}VertexNode, AdjList[MAX_VERTEX_NUM];

typedef struct
{
    AdjList adjlist;//邻接表
    int ves;//顶点
    int edge;//边
    int book[MAX_VERTEX_NUM];//判断是否有被访问过
}MGraph;

void menu();
void CreateGraph(Graph& G);//创建无向网 
int LocateVex(Graph& G, int v);//顶点的定位 
void Traverse(Graph& G); //网的遍历 
void CreatGraph(AdjGraph& G);//创建有向图邻接表
void ShowGraph(AdjGraph G);//输出邻接表
void createMGraph(MGraph* G);//创建无向图邻接表
void dfs(MGraph* G, int i);//无向图深度优先非递归遍历
void bfs(MGraph* G);//无向图广度优先递归遍历

void CreateGraph(Graph& G) {
    cout << "顶点个数:";
    cin >> G.vexnum;
    cout << "边数:";
    cin >> G.arcnum; //确定该网的顶点个数与边数
    cout << "点集:";
    for (int i = 0; i < G.vexnum; ++i) {
        cin >> G.vexs[i]; //对顶点进行赋值 
    }
    for (int i = 0; i < G.vexnum; ++i) {
        for (int j = 0; j < G.vexnum; ++j) {
            G.arcs[i][j] = MaxInt;//因为是网,所以先提前把每两个顶点之间的边的权值设为无穷大 
        }
    }
    cout << "构建无向网" << endl;
    for (int k = 0; k < G.arcnum; ++k) {
        char v1, v2;
        int w;
        cin >> v1 >> v2 >> w;//输入想要添加的边的权值以及边所依附的两顶点 
        int i = LocateVex(G, v1), j = LocateVex(G, v2);//确定两顶点在图中的位置
        G.arcs[i][j] = w;
        G.arcs[j][i] = G.arcs[i][j];//由于无向网具有对称性,因此可以对称赋值 
    }
}

int LocateVex(Graph& G, int v) {
    for (int i = 0; i < G.vexnum; ++i) {
        if (v == G.vexs[i])	return i;
    }
    return -1;
}

void Traverse(Graph& G) {
    for (int i = 0; i < G.vexnum; ++i)
        for (int j = 0; j < G.vexnum; ++j)
            if (G.arcs[i][j] != MaxInt)
                cout << "(" << G.vexs[i] << " " << G.vexs[j] << ") 权值:" << G.arcs[i][j] << endl;
}

//有向图邻接表
void CreatGraph(AdjGraph& G)//创建邻接表
{
    int tail, head, weight;
    cout << "输入图的顶点数和边数" << endl;
    cin >> G.n >> G.e;
    cout << "输入顶点:\n";
    for (int i = 0; i < G.n; i++)//输入顶点信息
    {
        cin >> G.VexList[i].data;
        G.VexList[i].adj = NULL;
    }
    cout << "逐条边输入,分别输入尾,头,权重:\n";
    for (int i = 0; i < G.e; i++)
    {
        cin >> tail >> head >> weight;//逐条边输入
        EdgeNode_youxiang* p = new EdgeNode_youxiang;
        p->dest = head; p->cost = weight;
        p->link = G.VexList[tail].adj;
        G.VexList[tail].adj = p;
    }
}
void ShowGraph(AdjGraph G)//输出邻接表
{
    for (int i = 0; i < G.n; i++)
    {
        cout << G.VexList[i].data;
        EdgeNode_youxiang* p = G.VexList[i].adj;
        while (p != NULL)
        {
            cout << " -> (" << p->dest << "," << p->cost << ")";
            p = p->link;
        }
        cout << "\n";
    }
}

//无向图邻接表
void createMGraph(MGraph* G)
{
    int i;
    int start;
    int end;

    EdgeNode* e;

    cout << "请输入顶点数与边数:" << endl;
    cin >> G->ves >> G->edge;
    //初始化
    cout << "请输入各顶点:" << endl;

    for (i = 0; i < G->ves; i++)//输入顶点
    {
        cin >> G->adjlist[i].ves;
        G->adjlist[i].firstedge = NULL;
    }
    //创建邻接矩阵

    cout << "请输入边:" << endl;;
    for (i = 0; i < G->edge; i++)
    {
        cin >> start >> end;

        e = (EdgeNode*)malloc(sizeof(EdgeNode));//分配空间
        e->adjves = end;
        e->next = G->adjlist[start].firstedge;
        G->adjlist[start].firstedge = e;//类似于链表的前插

        e = (EdgeNode*)malloc(sizeof(EdgeNode));//分配空间
        e->adjves = start;
        e->next = G->adjlist[end].firstedge;
        G->adjlist[end].firstedge = e;//类似于链表的前插
    }
}
//非递归深度遍历
void dfs(MGraph* G, int i)
{
    stack <int>s;
    EdgeNode* p;

    memset(G->book, 0, sizeof(G->book));//清空标志位

    G->book[i] = 1;
    s.push(i);
    cout << G->adjlist[i].ves;

    p = G->adjlist[i].firstedge;
    while (!s.empty())
    {
        p = G->adjlist[s.top()].firstedge;
        while (p)
        {
            if (G->book[p->adjves] == 0)
            {
                G->book[p->adjves] = 1;
                cout << G->adjlist[p->adjves].ves << " ";

                s.push(p->adjves);
                p = G->adjlist[p->adjves].firstedge;
            }
            else
                p = p->next;
        }
        if (p == NULL)
        {
            s.pop();
        }
    }
}
//广度遍历
void bfs(MGraph* G)
{
    int visited[MAX_VERTEX_NUM] = { 0 };
    int i;
    int k;

    EdgeNode* p;

    std::queue<int> q;

    for (i = 0; i < G->ves; i++)
    {
        visited[i] = 0;
    }
    for (i = 0; i < G->ves; i++)
    {
        if (visited[i] == 0)
        {
            visited[i] = 1;
            cout << G->adjlist[i].ves;
            q.push(i);

            while (!q.empty())
            {
                k = q.front();
                q.pop();

                p = G->adjlist[k].firstedge;

                while (p)
                {
                    if (visited[i] == 0)
                    {
                        visited[i] = 1;

                        cout << G->adjlist[p->adjves].ves <<" ";
                        q.push(p->adjves);
                    }
                    p = p->next;
                }

            }
        }
    }
}

void menu() {
    cout << "\n";
    cout << " ************************* 实验三  图 *************************\n";
    cout << " *                                                            *\n";
    cout << " *     1:创建无向图的邻接矩阵                                 *\n";
    cout << " *     2:创建有向图的邻接表                                   *\n";
    cout << " *     3:输出有向图的邻接表                                   *\n";
    cout << " *     4:(邻接表存储)实现无向图的深度优先非递归遍历         *\n";
    cout << " *     5:(邻接表存储)实现无向图的广度优先遍历               *\n";
    cout << " *     6:退出程序                                             *\n";
    cout << " *                                                            *\n";
    cout << " *************************************************************\n";
}

int  main()
{
    graph ga;
    int i, j;
    int num;
    AdjGraph G;
    MGraph D;
    MGraph B;
    menu();
    while (true)
    {
        cout << "☆请输入一个数字选项:";
        cin >> num;
        switch (num)
        {
        case 1: //创建无向图的邻接矩阵
        {
            cout<<"创建无向图的邻接矩阵:"<<endl;
            CreateGraph(ga);
            Traverse(ga);
            cout << endl;

        }break;
        case 2://创建有向图的邻接表
        {
            cout << "创建有向图的邻接表:" << endl;
            CreatGraph(G);
            cout << endl;
        }break;
        case 3://输出有向图的邻接表
        {
            ShowGraph(G);
            cout << endl;
        }break;
        case 4://无向图的深度优先非递归遍历DFS遍历
        {
            createMGraph(&D);
            cout << "无向图非递归深度优先遍历结果:" << endl;
            dfs(&D, 0); 
            cout << endl<<endl;
        }break;
        case 5://无向图的广度优先遍历
        {
            
            createMGraph(&B);
            cout << "无向图非递归深度优先遍历结果:" << endl;
            bfs(&B); 
            cout << endl<<endl;
        }break;
        case 6:
        {
            cout << "退出成功,欢迎下次使用!" << endl;
            return 0;
        }break;
        default:
            cout << "输入错误!请重新输入!" << endl;
        }
    }
}
  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值