数据结构实验:图遍历的演示

实验题目描述

1.基本要求

① 很多涉及图上操作的算法都是以图的遍历操作为基础的。试写一个程序,演示在连通的无向图上访问全部结点的操作。
② 以邻接多重表为存储结构,实现连通元向图的深度优先和广度优先遍历。以用户指定的结点为起点,分别输出每种遍历下的结点访问序列和相应生成树的边集。
③ 自测数据:教科书图7.33 暂时忽略里程,起点为北京。
④ 设图的结点不超过30个,每个结点用一个编号表示(如果一个图有n个结点,则它们的编号分别为1,2,···,n)。通过输入图的全部边输入一个图,每个边为一个数对,可以对边的输入顺序作出某种限制。注意,生成树的边是有向边,端点顺序不能颠倒。

2.参考

利用教科书《数据结构》(紫书)图7.33的数据。
在这里插入图片描述

实验过程

1.开发环境描述

1.开发环境:Visual Studio 2019
2.操作系统:win10

2.题目分析及解题思路

  • 问题抽象:
    图的遍历是图中非常基础的应用,学习图的遍历可以参考树的先根遍历和按层次遍历,深度优先搜索过程是树的先根遍历的推广,广度优先搜索过程是树的按层次遍历的推广。
  • 问题解决思路:
    ①应用深度优先DFS的算法:假设初始状态是图中所有顶点未曾被问,则深度优先搜索可从图中某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到;若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
    按照本人的理解,深度遍历适合使用栈实现,过程大致是根节点v入栈,输出v并获得v的其中一个邻接点w1,该邻接点W1入栈,重复上述过程,直到Wi无邻接点,Wi出栈,并寻找Wi-1的邻接点,若邻接点存在,重复上述过程,若邻接点不存在,则继续出栈,直到找到某个点有邻接点为止,重复上述过程。用这种方式可以输出无向连通图的所有结点。
    通过对BFS的分析过程,我们可以知道深度优先算法有极强的递归特性,因此考虑使用递归完成深度优先算法。
    ②应用广度优先BFS的算法:假设从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,就后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的顶点的邻接点都被访问。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上选过程,直至图中所有顶点都被访问到为止。
    按照本人的理解,广度遍历适合使用队列实现,过程大致是根节点v入队并输出,然后v出队,得到v所有的邻接点W1,W2…Wn,W1,W2…Wn入队并输出,然后W1出队并得到W1所有邻接点,重复上述过程,直到所有结点全部输出。总而言之,就是入队输出,出队则得邻接点,用这种方式可以输出无向连通图的所有结点。

3.实验代码

①处理交互的Interaction.h文件

#ifndef  INTERACTION_H
#define  INTERACTION_H

int Get_int();
void Initialize();
int Intercommand();
void Is_refresh();    

#endif // ! INTERACTION_H

处理交互的Interaction.cpp文件

#include"interaction.h"
#include<iostream>
#include<stdio.h>
#include<conio.h> 
using namespace std;
#define backColor 7
#define textColor 1
int Get_int()
{
	int num = 0, i = 0;
	char ch[11] = { 0 };
label1:
	num = 0;
	scanf_s("%s", ch, 10);
	i = 0;
	if (ch[0] == 45 || ch[0] == 43)
	{
		i++;
	}
	while (ch[i])
	{
		if (ch[i] < '0' || ch[i]>'9')
		{
			cout << "输入错误,请输入数字:" << endl;
			goto label1;
		}
		else {
			num = num * 10 + (int)ch[i] - 48;
		}
		i++;

	}
	if (ch[0] == 45)
	{
		return -num;
	}
	else
	{
		return num;
	}
}

void Initialize()
{
	system("cls");
	//设置背景颜色
	char command[9] = "color 07";		//默认颜色	
	command[6] = '0' + backColor;		//将backColor变量改为字符型(背景颜色设置为7,为白色) 
	command[7] = '0' + textColor;		//将textColor变量改为字符型 (背景颜色设置为1,为蓝色)
	system(command);				    //调用系统函数 
	printf("                        ***********交通网的深度遍历和广度遍历***********\n");
	printf("                        |    \t0. 退出                               |\n");
	printf("                        |    \t1. 直接通过文件读入地图               |\n");
	printf("                        |    \t2. 手动输入地图信息                   |\n");
	printf("                        ***********************************************\n");
}

int Intercommand()
{
	cout << "请输入您的选择:";
	int input;
	while (1)
	{
		int n = Get_int();
		if (n >= 0 && n < 3)
		{
			input = n;
			break;
		}

		cout << "输入错误,请重输:";
	}

	return input;
}

void Is_refresh()
{
	printf("\n");
	cout << "----------------------------------------------------" << endl;
	cout << "输入任意字符完成查看,回到菜单页面:" << endl;
	_getch();
}

② 处理队列操作的myquene.h

#ifndef MYQUENE_H
#define MYQUENE_H

typedef struct QENode
{
    int data;
    struct QENode* next;
}QENode;                  //队列结点 
typedef struct
{
    QENode* rear;
    QENode* front;
}LinkQueue;               //队列 

void InitQueue(LinkQueue* Q);           //初始化队列
void QueueAppend(LinkQueue* Q, int v);  //增加队列元素
void QueueDelete(LinkQueue* Q, int* v); //减少队列元素

#endif

处理队列操作的myquene.cpp

#include"myquene.h"
#include"graph.h"
#include<iostream>
#include<stdlib.h>
using namespace std;

void InitQueue(LinkQueue* Q)  //队列的初始化 
{
    Q->front = Q->rear = (QENode*)malloc(sizeof(QENode));
    Q->front->next = NULL;
}

void QueueAppend(LinkQueue* Q, int v)//入队列 
{
    QENode* p;
    p = (QENode*)malloc(sizeof(QENode));
    p->data = v;
    p->next = NULL;
    Q->rear->next = p;
    Q->rear = p;
}

void QueueDelete(LinkQueue* Q, int* v)//出队列 
{
    QENode* p;
    if (Q->front == Q->rear)
        return;
    p = Q->front->next;
    *v = p->data;
    Q->front->next = p->next;
    if (Q->rear == p)
        Q->rear = Q->front;
    free(p);
}

③处理图的处理的graph.h

#ifndef GRAPH_H
#define GRAPH_H
#include <string>
using namespace std;

#define MAX 30
//边结点
typedef struct ENode
{
    int ivex, jvex;            //该边依附的两个顶点在数组中的序号
    struct ENode* ilink;       //指向下一条依附于顶点ivex的边
    struct ENode* jlink;       //指向下一条依附于顶点jvex的边
}ENode;
//顶点结点
typedef struct VNode
{
    int mark;                //标记
    string data;             //顶点信息
    int number;              //顶点编号
    ENode* firstedge;
}VNode;
//图
typedef struct
{
    VNode AMlist[MAX];      //结点信息
    int v_num;              //顶点数
    int e_num;              //边数
}Graph;                    

void Initilized(Graph* graph);  //初始化图
void CreateGraph(Graph* graph); //创建图
void SetMark(Graph* graph);     //设置标记
void DFS(Graph* graph, int v);  //深度遍历
void BFS(Graph* graph, int u);  //广度遍历
void CreateGraph_by_file(Graph* graph);//通过文件创建图

#endif // !GRAPH_H


处理图的处理的graph.cpp

#include"graph.h"
#include"myquene.h"
#include<iostream>
#include"interaction.h"
#include<fstream>
#include<string>
using namespace std;

void Initilized(Graph* graph)//图的初始化
{
    graph = (Graph*)malloc(sizeof(Graph));
    graph->v_num = 0;
    graph->e_num = 0;
}
int graph_flag = 0;
void CreateGraph(Graph* graph)//图的创建图
{
    ENode* p, * q, * e;
    e = NULL;
    int i,input_flag=0;
    printf("请输入连通无向图的顶点数 :");
    graph->v_num = Get_int();
    do{
        if (input_flag++ > 1)
            printf("输入错误,请重新输入\n");
        printf("请输入连通无向图的边数 :");
        graph->e_num = Get_int();
    } while (graph->e_num < graph->v_num - 1);
    

    for (i = 1; i <= graph->v_num; i++)
    {
        printf("请输入第%d个顶点的信息:\n", i);
        cin >> graph->AMlist[i].data;
        graph->AMlist[i].number = i;
        graph->AMlist[i].firstedge = NULL;
        graph->AMlist[i].mark = 0;
    }     
    //输入每个点的信息
    for (i = 1; i <= graph->e_num; i++)
    {
        p = (ENode*)malloc(sizeof(ENode));
        printf("请输入第%d条边的首尾两个点(编号小的在前 (例如1 3回车))\n",i);
        while (1)
        {
            p->ivex = Get_int();
            p->jvex = Get_int();
            if (p->ivex<p->jvex && p->ivex>0 && p->jvex <= graph->v_num && p->ivex!=p->jvex)
                break;
            else {
                cout << "您输入错误,请重新输入" << endl;
            }
        }
        p->ilink = p->jlink = NULL;                    //边信息填入完毕
        /*将边连在一起*/
        if (graph->AMlist[p->ivex].firstedge == NULL)
            graph->AMlist[p->ivex].firstedge = p;      //邻接多重表的顶点首指针指向p
        else
        {
            q = graph->AMlist[p->ivex].firstedge;     //记录首边
            while (q != NULL)
            {
                e = q;
                if (q->ivex == p->ivex)    
                    q = q->ilink;
                else
                    q = q->jlink;
            }
            if (e->ivex == p->ivex)
                e->ilink = p;
            else
                e->jlink = p;
        }
        if (graph->AMlist[p->jvex].firstedge == NULL)
            graph->AMlist[p->jvex].firstedge = p;
        else
        {
            q = graph->AMlist[p->jvex].firstedge;
            while (q != NULL)
            {
                e = q;
                if (q->ivex == p->ivex)
                    q = q->ilink;
                else
                    q = q->jlink;
            }
            if (e->ivex == p->ivex)
                e->ilink = p;
            else
                e->jlink = p;
        }
    }
}

void SetMark(Graph* graph)//设置访问标记
{
    int i;
    for (i = 1; i <= graph->v_num; i++)
        graph->AMlist[i].mark = 0;
}

void DFS(Graph* graph, int v)//深度遍历
{
    ENode* p;
    cout << graph->AMlist[v].data <<"        "; graph_flag++;
    graph->AMlist[v].mark = 1;
    p = graph->AMlist[v].firstedge;
    while (p != NULL)
    {
        if (p->ivex == v)
        {
            if (graph->AMlist[p->jvex].mark == 0)
            {
                cout << "<" << p->ivex << "," << p->jvex << ">" << endl;
                DFS(graph, p->jvex);
            }
                
            p = p->ilink;
        }
        else
        {
            if (graph->AMlist[p->ivex].mark == 0)
            {
                cout << "<" << p->jvex << "," << p->ivex << ">" << endl;
                DFS(graph, p->ivex);
                
            }
                
            p = p->jlink;
        }
    }
}

void BFS(Graph* graph, int u)//广度遍历
{
    LinkQueue Q;
    ENode* p;
    InitQueue(&Q);
    cout << graph->AMlist[u].data << endl;
    graph->AMlist[u].mark = 1;
    QueueAppend(&Q, u);
    while (Q.front != Q.rear)
    {
        QueueDelete(&Q, &u);
        p = graph->AMlist[u].firstedge;
        while (p != NULL)
        {
            if (p->ivex == u)
            {
                if (graph->AMlist[p->jvex].mark == 0)
                {
                    QueueAppend(&Q, p->jvex);
                    graph->AMlist[p->jvex].mark = 1;
                    cout << graph->AMlist[p->jvex].data << endl;
                }
                p = p->ilink;
            }
            else
            {
                if (graph->AMlist[p->ivex].mark == 0)
                {
                    QueueAppend(&Q, p->ivex);
                    graph->AMlist[p->ivex].mark = 1;
                    cout << graph->AMlist[p->ivex].data << endl;
                }
                p = p->jlink;
            }
        }
    }
}

void CreateGraph_by_file(Graph* graph)
{
    fstream fp;
    fp.open("graph_info.txt", ios::in);
    fp >> graph->v_num; //cout << graph->v_num;
    fp.seekg(1, ios::cur);
    fp >> graph->e_num; //cout << graph->e_num;
    fp.seekg(2, ios::cur);
    //cout << graph->v_num << " " << graph->e_num << endl;
   
    char v_info[10]; 
    int i = 0;
    while (++i <= graph->v_num &&  fp.getline(v_info, 100,'\n') ) // line中不包括每行的换行符  
    {
        graph->AMlist[i].number = i;
        graph->AMlist[i].firstedge = NULL;
        graph->AMlist[i].mark = 0;
        graph->AMlist[i].data=v_info;
        //cout << graph->AMlist[i].data<<endl;
    }
    ENode* p, * q, * e;
    e = (ENode*)malloc(sizeof(ENode));
    q = (ENode*)malloc(sizeof(ENode));
    for (i = 1; i <= graph->e_num; i++)
    {
        p = (ENode*)malloc(sizeof(ENode));
        fp >> p->ivex; //cout << p->ivex<<endl;
        fp.seekg(1, ios::cur);
        fp >> p->jvex; //cout << p->jvex<<endl;
        fp.seekg(2, ios::cur);
        p->ilink = p->jlink = NULL;                    //边信息填入完毕
        /*将边连在一起*/
        if (graph->AMlist[p->ivex].firstedge == NULL)
            graph->AMlist[p->ivex].firstedge = p;      //邻接多重表的顶点首指针指向p
        else
        {
            q = graph->AMlist[p->ivex].firstedge;     //记录首边
            while (q != NULL)
            {
                e = q;
                if (q->ivex == p->ivex)
                    q = q->ilink;
                else
                    q = q->jlink;
            }
            if (e->ivex == p->ivex)
                e->ilink = p;
            else
                e->jlink = p;
        }
        if (graph->AMlist[p->jvex].firstedge == NULL)
            graph->AMlist[p->jvex].firstedge = p;
        else
        {
            q = graph->AMlist[p->jvex].firstedge;
            while (q != NULL)
            {
                e = q;
                if (q->ivex == p->ivex)
                    q = q->ilink;
                else
                    q = q->jlink;
            }
            if (e->ivex == p->ivex)
                e->ilink = p;
            else
                e->jlink = p;
        }
    }
    fp.close();
}

④源.cpp

#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include"graph.h"
#include"myquene.h"
#include"interaction.h"
using namespace std;

int main()
{
    int v1, v2;  //定义两个顶点
    Graph graph; //定义了一个图
    int choose;
    
    while (1)
    {
        Initialize();
        fflush(stdin);
        choose = Intercommand();
        switch (choose)
        {
        case 0:
            exit(0);
        case 2:
            Initilized(&graph);
            CreateGraph(&graph);
            printf("\n输入深度广度遍历的起始点:\n");
            v2 = Get_int();
            v1 = v2;
            printf("\n深度遍历序列:\n顶点序列:\n");
            SetMark(&graph);
            DFS(&graph, v2);
            printf("\n广度遍历序列:\n顶点序列:\n");
            SetMark(&graph);
            BFS(&graph, v1);
            Is_refresh();
        case 1:
            Initilized(&graph);
            CreateGraph_by_file(&graph);
            fflush(stdin);
            printf("\n输入深度广度遍历的起始点:\n");
            v2 = Get_int();
            v1 = v2;
            printf("\n深度遍历序列:\n");
            SetMark(&graph);
            DFS(&graph, v2);
            printf("\n广度遍历序列:\n");
            SetMark(&graph);
            BFS(&graph, v1);
            Is_refresh();
        }
    }
    
    return 0;
}

4.文件

  • graph-info.txt内容
    25 30
    Urumchi
    Sining
    Lanzhou
    Hohhot
    BeiJing
    Tianjin
    Shenyang
    Changchun
    Harbin
    Dalian
    Xian
    Zhengzhou
    Xuzhou
    ChengDu
    Wuhan
    Shanghai
    Kunming
    Guiyang
    Zhuzhou
    Nanchang
    Fuzhou
    Nanning
    Liuzhou
    Guangzhou
    Shenzhen
    1 3
    2 3
    3 4
    3 11
    11 12
    4 5
    5 12
    12 13
    5 6
    6 13
    6 7
    7 10
    7 8
    8 9
    11 14
    14 17
    14 18
    17 18
    12 15
    22 23
    18 23
    18 19
    19 23
    19 24
    24 25
    19 20
    20 21
    16 20
    13 16
    15 19

联系我

如果对代码有改进想法的或者需要源代码的,可联1274669808@qq.com

  • 25
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值