【数据结构】邻接矩阵和邻接图的遍历

news2024/11/19 6:40:33

写在前面

本篇文章开始学习数据结构的图的相关知识,涉及的基本概念还是很多的。

本文的行文思路:

  • 学习图的基本概念

  • 学习图的存储结构——本文主要介绍邻接矩阵和邻接表

  • 对每种结构进行深度优先遍历和广度优先遍历


先识概念

话不多说,狠活献上


学习思想

等等,先别急,正式学习之前先认识几个英语单词及缩写


类型(Type)
顶点(vertex)
边(Edge)
邻接(adjacency,简写adj)
邻接矩阵(adjacency Matrix)
邻接表(adjacency List)
深度优先遍历(Depth First Search,简称BFS)
广度优先遍历(Breadth First Search,简称DFS)

邻接矩阵的存储结构

typedef char VertexType;  //顶点类型
typedef int EdgeType;     //边类型
#define MAXVEX 100        //最大顶点数目
#define INFINITY 65535    //用65535表示无穷


//邻接矩阵的存储结构

typedef struct
{
    VertexType vexs[MAXVEX];       //顶点数组
    EdgeType arc[MAXVEX][MAXVEX];  //边数组
    int numVertexes, numEdges;        //当前顶点的顶点数和边数
}MGraph;

邻接表的存储结构

#define MAXVEX 100
typedef char VertexType;   //顶点类型
typedef int EdgeType;      //边上的权值类型

int visited[MAXVEX];

typedef struct EdgeNode   //边表结点
{
    int adjvex;           //存储该顶点对应的下标
    EdgeType info;        //存储权值,非网图则无需
    struct EdgeNode* next;
}EdgeNode;

typedef struct VertexNode  //顶点结点
{
    VertexType data;       //存储顶点信息
    EdgeNode* firstedge;   //边表头指针
}VertexNode, AdjList[MAXVEX];

//邻接表存储结构

typedef struct
{
    AdjList adjList;
    int numVertexes, numEdges; //当前顶点数目和边数
}GraphAdjList;

再学应用

邻接矩阵的深度遍历和广度遍历

  • 深度遍历:实际上就是二叉树的前序遍历

  • 广度遍历:实际上就是二叉树的层序遍历,要用到队列,我们自己还要写出队列的一些基本操作

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


typedef char VertexType;  //顶点类型
typedef int EdgeType;     //边类型
#define MAXVEX 100        //最大顶点数目
#define INFINITY 65535    //用65535表示无穷


//邻接矩阵的存储结构

typedef struct
{
    VertexType vexs[MAXVEX];       //顶点数组
    EdgeType arc[MAXVEX][MAXVEX];  //边数组
    int numVertexes, numEdges;        //当前顶点的顶点数和边数
}MGraph;


//邻接矩阵的初始化

void CreateMGraph(MGraph* G)
{
    int i, j, k;
    printf("请输入顶点的个数和边数:");
    scanf("%d%d", &G->numVertexes, &G->numEdges);

    for (i = 0; i < G->numVertexes; i++)
    {
        printf("请输入%d个顶点: ", i + 1);
        scanf("%s", &G->vexs[i]);
    }

    //将矩阵的所有数据初始化为"无穷"

    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[i][j] = INFINITY;

    //然后自定义矩阵中的数据

    for (k = 0; k < G->numEdges; k++)
    {
        printf("请输入边(vi,vj)中的下标i和j: ");
        scanf("%d%d", &i, &j);
        G->arc[i][j] = 1;
        G->arc[j][i] = G->arc[i][j];    //无向图的邻接矩阵沿着右对角线对称
    }
}


int visited[MAXVEX];                 //访问标志的数组

//深度优先递归算法

void DFS(MGraph G, int i)            
{
    int j;
    visited[i] = 1;                   //将第i个顶点标记为已访问
    printf("%c ", G.vexs[i]);          //打印顶点,也可以是其他操作

    //循环遍历G中所有的顶点
    for (j = 0; j < G.numVertexes; j++)
    {
        //判断当前正在遍历的顶点j和顶点i是否相邻且未被访问过,相连为1,不相连为0(前提是不带权的图)
         if (G.arc[i][j] == 1 && !visited[j])  
            DFS(G, j);
    }
        
}


//邻接矩阵的深度遍历操作

void DFSTraverse(MGraph G)
{
    int i;
    //初始化所有顶点状态都是未访问过的 
    for (i = 0; i < G.numVertexes; i++)
    {
        visited[i] = 0;
    }
    
    //对所有未访问过的顶点调用DFS,若为连通图则只执行一次
    for (i = 0; i < G.numVertexes; i++)
    {
        if (!visited[i])
            DFS(G, i);
    }
}

//队列的顺序存储结构
typedef struct
{
    char data[MAXVEX];
    int front;
    int rear;
}SqQueue;

void InitQueue(SqQueue* Q)
{
    Q->front = 0;
    Q->rear = 0;
}

void EnQueue(SqQueue* Q,int e)
{
    if ((Q->rear + 1) % MAXVEX == Q->front)
        return;
    Q->data[Q->rear] = e;
    Q->rear = (Q->rear + 1) % MAXVEX;
}

void DeQueue(SqQueue* Q, int* e)
{
    if (Q->front == Q->rear)
        return;
    *e = Q->data[Q->front];
    Q->front = (Q->front + 1) % MAXVEX;

}

int QueueEmpty(SqQueue Q)
{
    return Q.front == Q.rear;
}

//邻接矩阵的广度遍历

void BFSTraverse(MGraph G)
{
    int i, j;
    SqQueue Q;
    for (i = 0; i < G.numVertexes; i++)
    {
        visited[i] = 0;            //将每一个顶点都设置未访问
    }

    InitQueue(&Q);                 //初始化一个辅助用的队列

    for (i = 0; i < G.numVertexes; i++)
    {
        if (!visited[i])
        {
            visited[i] = 1;         //设置当前顶点为已访问
            printf("%c ", G.vexs[i]);

            EnQueue(&Q, i);         //将此顶点入队列
            while (!QueueEmpty(Q))
            {
                DeQueue(&Q, &i);    //首元素出队,赋给i

                for (j = 0; j < G.numVertexes; j++)
                {
                    if (G.arc[i][j] == 1 && !visited[j])  //边存在且未被访问过
                    {
                        visited[j] = 1;                  //设置当前顶点为已访问
                        printf("%c ", G.vexs[j]);        //打印顶点
                        EnQueue(&Q, j);                  //将此顶点入队
                    }
                }
            }
        }
    }
}

int main()
{
    MGraph G;
    CreateMGraph(&G);
    printf("DFS遍历顺序:");
    DFSTraverse(G);
    printf("\n");

    printf("BFS遍历顺序:");
    BFSTraverse(G);
    printf("\n");
    return 0;
}

邻接表的深度遍历和广度遍历

#define _CRT_SECURE_NO_WARNINGS 1
//图的遍历主要有两种,深度优先遍历和广度优先遍历
//深度优先遍历实际上是二叉树的先序遍历,广度优先遍历实际上是二叉树的层序遍历

#include<stdio.h>
#include<malloc.h>
#define MAXVEX 100
typedef char VertexType;   //顶点类型
typedef int EdgeType;      //边上的权值类型

int visited[MAXVEX];

typedef struct EdgeNode   //边表结点
{
    int adjvex;           //存储该顶点对应的下标
    EdgeType info;        //存储权值,非网图则无需
    struct EdgeNode* next;
}EdgeNode;

typedef struct VertexNode  //顶点结点
{
    VertexType data;       //存储顶点信息
    EdgeNode* firstedge;   //边表头指针
}VertexNode, AdjList[MAXVEX];

//邻接表存储结构

typedef struct
{
    AdjList adjList;
    int numVertexes, numEdges; //当前顶点数目和边数
}GraphAdjList;


//邻接表的初始化
void CreateALGraph(GraphAdjList* GL)
{
    int i, j, k;
    EdgeNode* e;
    printf("输入顶点数和边数:");
    scanf("%d%d", &GL->numVertexes, &GL->numEdges);


    //将数据存进顶点数组,把顶点指针域置空
    for (i = 0; i < GL->numVertexes; i++)
    {
        getchar();
        printf("请输入第%d个顶点:", i + 1);
        scanf("%c", &GL->adjList[i].data);
        GL->adjList[i].firstedge = NULL;
    }


    for (k = 0; k < GL->numEdges; k++)
    {
        printf("输入边(vi,vj)上的顶点序号:");
        scanf("%d%d", &i, &j);

        //结点i记录结点j的下标
        e = (EdgeNode*)malloc(sizeof(EdgeNode));
        e->adjvex = j;
        e->next = GL->adjList[i].firstedge; //下面两步相当于链表的头插法
        GL->adjList[i].firstedge = e;

        //结点j记录结点i的下标
        e = (EdgeNode*)malloc(sizeof(EdgeNode));
        e->adjvex = i;
        e->next = GL->adjList[j].firstedge;  //下面两步相当于链表的头插法
        GL->adjList[j].firstedge = e;
    }

}


//邻接表的深度优先递归算法

void DFS(GraphAdjList GL, int i)
{
    EdgeNode* p;
    visited[i] = 1;
    printf("%c ", GL.adjList[i].data);
    p = GL.adjList[i].firstedge;
    while (p)
    {
        if (!visited[p->adjvex])
            DFS(GL, p->adjvex);
        p = p->next;
    }
}

//邻接表的深度遍历操作
void DFSTraverse(GraphAdjList GL)
{
    int i;
    for (i = 0; i < GL.numVertexes; i++)
    {
        visited[i] = 0;
    }
    for (i = 0; i < GL.numVertexes; i++)
    {
        if (!visited[i])
            DFS(GL, i);
    }
}


//队列的顺序存储结构
typedef struct
{
    char data[MAXVEX];
    int front;
    int rear;
}SqQueue;

void InitQueue(SqQueue* Q)
{
    Q->front = 0;
    Q->rear = 0;
}

void EnQueue(SqQueue* Q, int e)
{
    if ((Q->rear + 1) % MAXVEX == Q->front)
        return;
    Q->data[Q->rear] = e;
    Q->rear = (Q->rear + 1) % MAXVEX;
}

void DeQueue(SqQueue* Q, int* e)
{
    if (Q->front == Q->rear)
        return;
    *e = Q->data[Q->front];
    Q->front = (Q->front + 1) % MAXVEX;

}

int QueueEmpty(SqQueue Q)
{
    return Q.front == Q.rear;
}


//邻接表的广度优先遍历
void BFSTraverse(GraphAdjList GL)
{
    int i;
    EdgeNode* p;
    SqQueue Q;
    Q.front = Q.rear = 0;
    for (i = 0; i < GL.numVertexes; i++)
    {
        visited[i] = 0;
        if (!visited[i])
        {
            visited[i] = 1;
            printf("%c ", GL.adjList[i].data);
            EnQueue(&Q, i);
            while (!QueueEmpty(Q))
            {
                DeQueue(&Q, &i);
                p = GL.adjList[i].firstedge;
                while (p)
                {
                    if (!visited[p->adjvex])
                    {
                        visited[p->adjvex] = 1;
                        printf("%c ", GL.adjList[p->adjvex].data);
                        EnQueue(&Q, p->adjvex);
                    }
                    p = p->next;
                }
            }
        }
    }
}


int main()
{
    GraphAdjList GL;
    CreateALGraph(&GL);
    printf("DFS遍历顺序:");
    DFSTraverse(GL);
    printf("\n");

    printf("BFS遍历顺序:");
    BFSTraverse(GL);
    printf("\n");
    return 0;
}

一点补充

下面补充一个小知识点,就是typedef定义数组类型,先看下面的代码是什么意思呢?
typedef struct VertexNode AdjList[MAXVEX];
上面语句的意思是:定义一个元素类型是 struct VertexNode,含有MAXVEX个元素的数组类型

换个例子:
typedef int vector[10];
vector v1,v2;

语句定义了vector类型的两个对象v1和v2,每个对象都是vector类型的一个数组,每个数组由10个整型元素所组成。

写在最后

上面的代码难免会有疏漏,如果各位大佬发现错误,请一定要指正🙏

👍🏻 点赞,你的认可是我创作的动力!
收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/388604.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ Stack栈学习

1. stack的介绍和使用1.1 stack的介绍1. stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。2. stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特定类封装作为其底层的容器&…

sync map思考

sync map 作为解决 map 并发读写问题的补充&#xff0c;用法上其实不复杂&#xff0c;有些惋惜的是&#xff0c;不支持 len 统计数量的方法。map 并发读写算得上一个非常严重的问题&#xff0c;会导致服务宕机&#xff0c;为了避免 map 的并发读写&#xff0c;一种解决办法是直…

华为机试题:HJ108 求最小公倍数(python)

文章目录&#xff08;1&#xff09;题目描述&#xff08;2&#xff09;Python3实现&#xff08;3&#xff09;知识点详解1、input()&#xff1a;获取控制台&#xff08;任意形式&#xff09;的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…

软件工程知识-软件测试

1、软件测试是发现软件错误&#xff08;缺陷&#xff09;的主要手段&#xff1a; 从是否关系软件内部结构和具体实现的角度对软件测试进行分类 2.静态测试&#xff1a;以检查为主&#xff08;桌前检查、代码走查、代码审查&#xff09; 动态测试&#xff1a;实际运行程序&am…

Leetcode刷题一

目录序言树「结构」「遍历」「经验」「跨父节点」「题型」序言 笔记根据labuladong进行总结&#xff0c;极力推荐labuladong算法进行学习&#xff01;&#xff01; 树 0、算法一开始就应该刷树&#xff0c;了解递归的思想。 1、C语言中定义了一个结构体&#xff0c;然后申明…

通过指针引用数组的几种方法的原理和差异;以及利用指针引用数组元素的技巧

关于地址&#xff0c;指针&#xff0c;指针变量可以参考这篇文章&#xff1a; 地址&#xff0c;指针&#xff0c;指针变量是什么&#xff1f;他们的区别&#xff1f;符号&#xff08;*&#xff09;在不同位置的解释&#xff1f;_juechen333的博客-CSDN博客https://blog.csdn.n…

nginx http模块

模块依赖2. 模块的初始化2.1 location的定义location的定义包含以下几种location [ | ~ | ~* | ^~ ] uri { ... } location name { ... }:表示精确匹配&#xff0c;只有请求的url路径与后面的字符串完全相等时&#xff0c;才会命中&#xff0c;不支持location嵌套~&#xff1a…

有免费的PDF转Word吗?值得收藏的7个免费 PDF转Word工具请收好

PDF 和 DOC 是人们在工作中广泛使用的两种最流行的文档格式。PDF 是 Adobe 的便携式文档格式&#xff0c;DOC 是 Microsoft 的 Word 文档格式。PDF 是一种更安全可靠的文件格式&#xff0c;因为它很难编辑 PDF 文件&#xff0c;但是有一些称为 PDF 编辑器的工具可用于编辑 PDF …

【Linux】工具(4)——make/Makefile

本期博客我们的任务就是搞懂自动化构建工具——make/Makefile一、什么是make/Makefile&#x1f4cc;make是一个命令工具&#xff0c;是一个解释makefile中指令的命令工具&#xff0c;一般来说&#xff0c;大多数的IDE都有这个命令&#xff0c;比如&#xff1a;Delphi的make&…

设计模式之创建型模式

一、设计模式分类二、创建型模式1、单例模式1.1 饿汉式&#xff08;静态变量&#xff09;1.2 饿汉模式&#xff08;静态代码块&#xff09;1.3 懒汉式&#xff08;线程不安全&#xff09;1.4 懒汉式&#xff08;线程安全&#xff0c;同步方法&#xff09;1.5懒汉式&#xff08;…

PHP语言

一、PHP简介 什么是 PHP&#xff1f; PHP 是强有力的服务器端脚本语言 PHP 是免费的&#xff0c;并且使用广泛 PHP能够包含文本、HTML、CSS以及PHP代码&#xff0c;在服务器上执行&#xff0c;结果以纯文本返回浏览器。PHP是从C和Perl发展而来的一种非常简单的语言&#xff…

考研流程,可以进来转一转(考研你不知道的事情)(详细版)

之前有听过好多人说要考研&#xff0c;那么&#xff0c;考研的信息&#xff0c;如何获取呢&#xff0c;考研都有哪些流程呢。 初试开始到考试&#xff1a;↓ 1、了解考研信息。 2、确定自己要报考的专业。&#xff08;本专业or跨考&#xff09; 3、选择地区 4、选择要报考的学…

TAURI初体验

TAURI初体验 - 一个聊天机器人demo前言一、搭建Tauri apps二、引入库相关vue库三、 业务逻辑1.页面布局2. openai的初始化3. text-davinci-003模型4.gpt-3.5-turbo模型总结前言 一直使用Electron开发桌面应用&#xff0c;时间长了也就腻了。很早之前就关注了Tauri&#xff0c;趁…

尚硅谷SpringCloud Alibaba

1. Cloud Alibaba简介2.Nacos简介和下载3.Nacos安装4.Nacos之服务提供者注册5.Nacos之服务消费者注册和负载6.Nacos服务注册中心对比提升7.Nacos之服务配置中心8.Nacos之命名空间分组和DataID三者关系9.Nacos之DataID配置10.Nacos之Group分组方案11.Nacos之Namespace空间方案12…

基于支持向量机SVM的分类预测,基于SVM的雷击故障识别

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 SVM应用实例,基于SVM的雷击故障分类预测 支持向量机SVM的详细原理 SVM的定义 支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特征空…

主流机器学习平台调研与对比分析

梗概 本报告主要调研目前主流的机器学习平台&#xff0c;包括但不限于Amazon的Sage maker&#xff0c;Alibaba的PAI&#xff0c;Baidu的PaddlePaddle。对产品的定位、功能、实践、定价四个方面进行详细解析&#xff0c;并通过标杆对比分析提出一套机器学习平台评价体系&#x…

案例总结之——redis缓存不一致的问题

1.是什么&#xff1f; Redis&#xff1a;Remote Dictionary Server&#xff0c;即远程字典服务 支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。Redis 是当前互联网世界最流行的 NoSQL&#xff08;Not Only SQL&#xff09;数据库…

js实现懒加载 最简单

实现前得先知道一下js里的几个高度&#xff1a; 视口高度&#xff1a;document.documentElement.clientHeight 滚动高度&#xff1a;document.documentElement.scrollTop || document.body.scrollTop 元素距离最顶端高度&#xff1a;dom元素.offsetTop 先说一下思路&#xff1…

k8s之list-watch,节点调度以及亲和性

一、list-watch机制1.1 list-watch介绍Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面建立 Po…

【mmdeploy部署系列】使用Tensorrt加速部署mmpose人体姿态库

【mmdeploy部署系列】使用Tensorrt加速部署mmpose人体姿态库0.引言1.安装mmcv2.使用mmpose&#xff08;1&#xff09;安装mmpose&#xff08;2&#xff09;运行mmpose3.使用mmdeploy&#xff08;1&#xff09;安装ppl.cv&#xff08;2&#xff09;编译安装mmdeploy&#xff08;…