图的邻接矩阵实现以及遍历

图的邻接矩阵实现以及遍历

文章目录

  • 图的邻接矩阵实现以及遍历
    • 一、前言
    • 二、图的遍历
      • [2.1 深搜( D F S DFS DFS)](#2.1 深搜( D F S DFS DFS))
        • [2.1.1 概念](#2.1.1 概念)
        • [2.1.2 思想](#2.1.2 思想)
        • [2.1.3 过程](#2.1.3 过程)
      • [2.2 广搜( B F S BFS BFS)](#2.2 广搜( B F S BFS BFS))
        • [2.2.1 概念](#2.2.1 概念)
        • [2.2.2 过程](#2.2.2 过程)
    • 三、邻接矩阵的实现
      • [3.1 定义图结构](#3.1 定义图结构)
      • [3.2 初始化](#3.2 初始化)
      • [3.3 添边](#3.3 添边)
      • [3.4 访问](#3.4 访问)
      • [3.5 深搜](#3.5 深搜)
      • [3.6 广搜](#3.6 广搜)
    • 小结

一、前言

上文图的基本概述中已经对图的存储结构进行了一个深入解析,但是具体怎么实现 呢?今天我就来挑战一下~当有了图,就像对树一样,我们需要对图中的元素进行访问处理 ,因此需要遍历这一关键的操作,那这又该怎样实现呢?且停留片刻,相信你一定能摸清里面的门道~

二、图的遍历

同样作为非线性结构,图的遍历和树的遍历思想基本一致 ,其遍历可分为:深搜 ( D F S DFS DFS),广搜 ( B F S BFS BFS)

唯一的区别 :图的元素与元素之间是: m : n m:n m:n,会出现重复遍历已访问过的节点。而树不会出现这个问题(往上看只有一个元素)。接下来我将对其进行深入解读~

2.1 深搜( D F S DFS DFS)

2.1.1 概念

一条道走到黑。

2.1.2 思想

递归

2.1.3 过程

首先,有一个既定的原则 :节点的访问由小到大 ( V 1 V1 V1, V 2 V2 V2​​...)。

为了防止重复遍历,需要引入visited数组,记录已经处理的节点。
如图

从 V 1 V1 V1开始,可以到 V 2 , V 3 V2,V3 V2,V3,由于从小到大,因此到 V 2 V2 V2。 V 2 V2 V2可以到 V 1 , V 4 , V 5 V1,V4,V5 V1,V4,V5, V 1 V1 V1遍历过了,于是到 V 4 V4 V4。继续遍历,直到 V 5 V5 V5,无路可走,只能回溯,每回退一次,查看是否有遗漏的没遍历的路径,直到最后一个点。

最终结果 : V 1 − > V 2 − > V 4 − > V 8 − > V 5 − > V 3 − > V 6 − > V 7 V1->V2->V4->V8->V5->V3->V6->V7 V1−>V2−>V4−>V8−>V5−>V3−>V6−>V7

2.2 广搜( B F S BFS BFS)

2.2.1 概念

广搜是层次遍历 ,就像是"水面波纹"、"病毒感染",由里及外地探索可以触及的点(由本身的点能连接到的点)。

2.2.2 过程

但是基于遍历本身需要有先后之分,因此需要对"可触及的点"进行一个临时存储 ------队列。

同时在访问过程中,同样需要定义一个visited数组,记录已进入队列 的(只要进过队列,就进行记录),以防止重复遍历。

每出队一个点(进行处理),就继续将其"所能接触到的点"入队。

如图

V 1 V1 V1入队(标记为已访问), V 1 V1 V1出队(处理 V 1 V1 V1), V 2 V2 V2, V 3 V3 V3入队(标记), V 2 V2 V2出队(处理), V 4 V4 V4, V 5 V5 V5入队(标记),以此不断循环。

最终结果 : V 1 − > V 2 − > V 3 − > V 4 − > V 5 − > V 6 − > V 7 − > V 8 V1->V2->V3->V4->V5->V6->V7->V8 V1−>V2−>V3−>V4−>V5−>V6−>V7−>V8

三、邻接矩阵的实现

3.1 定义图结构

c 复制代码
// 定义数量------便于申请时预留空间
#define MaxNodeNum 20
// 定义一个足够大的值(表示边的权值)
#define INF		   1E4
// 邻接矩阵的顶点结构
typedef struct
{
    int no;			// 顶点编号
    char *show;		// 显示顶点信息
} MatrixVertex;
// 表示边
typedef int MatrixEdge;
// 图的邻接矩阵表示法
typedef struct
{
    MatrixVertex vex[MaxNodeNum];					// 点集(一维数组)
    int NodeNum;									// 对数量进行限制
    MatrixEdge edges[MaxNodeNum][MaxNodeNum];		// 边集(矩阵)
    int edgeNum;									// 边的数量
    int directed;									// 表示是否有向
} MGraph;

3.2 初始化

初始化点集合边集
如图

c 复制代码
// 初始化图
void initMGraph(MGraph *graph, char *names[], int num, int directed, int edgeValue)
{
    graph->directed = directed;
    graph->nodeNum = num;			// 自己定义好的
    graph->edgeNum = 0;				// 边需要自己建立
    // 初始化点集
    memset(graph->vex, 0, sizeof(graph->vex));
    // 初始化边集,当节点数量为n时,边的最大数量为n^2(每一个顶点都可以和其他任意的顶点建立边,
    // 包括它自己)
    memset(graph->edges, 0, sizeof(MatrixEdge) * MaxNodeNum * MaxNodeNum);
    for(int i = 0; i < num; ++i)
    {
        // 初始化顶点信息
        graph->vex[i].no = i;
        graph->vex.show = names[i];
        // 初始化边
        for(int j = 0; j < num; ++j)
        {
            graph->edges[i][j] = edgeValue;		// 二维数组中存在的值(表示边的连接情况)
        }
    }
}

3.3 添边

  • 判断边的合法性
  • 填充矩阵edgeValue

如图(最终)

c 复制代码
// 判断边
static int isEdge(int weight)
{
    if(weight > 0 && weight < INF)
    {
        return 1;
    }
    return 0;
}
// 添加边
void addMGraphEdge(MGraph *graph, int x, int y, int w)
{
    // 判断横坐标是否合法
    if(x < 0 || x > graph->nodeNum)
    {
        return;
    }
    // 判断纵坐标是否合法
    if(y < 0 || y > graph->nodeNum)
    {
        return;
    }
    if(!isEdge(graph->edges[x][y]))
    {
        graph->edges[x][y] = w;
        if(graph->directed == 0)
        {
            // 只有当图为无向图时,才需要存储这个信息
            graph->edges[y][x] = w;
        }
        graph->edgeNum++;
    }
}

3.4 访问

easy~

c 复制代码
void visitMGraphNode(MatrixVertex *node)
{
    printf("\t%s", node->show);
}

3.5 深搜

  • 访问顶点,标记顶点
  • 从上述顶点出发,继续遍历可访问的顶点(按从小到大的顺序),循环上述过程

如图

c 复制代码
// 定义已访问的空间(全局变量,BFS也会用到)
static int MGraphVisited[MaxNodeNum];
// 深搜
void DFS_MGraph(MGraph *graph, int v)
{
    // 访问
    visitMGraphNode(graph->vex[v]);
    // 标记
    MGraphVisited[v] = 1;
    for(int i = 0; i < graph->nodeNum; ++i)
    {
        if(isEdge(graph->edges[v][i]) && MGraphVisited[i] == 0)
        {
            DFS_MGraph(graph, i);	// 递归
        }
    }
}

3.6 广搜

  • 定义一个临时队列
  • 入队顶点,标记为已访问
  • 出队顶点,访问,(从该顶点出发)入队可访问的顶点
  • 循环上述过程

如图

c 复制代码
// 注意:当同时调用DFS和BFS时,因为两种遍历都需要用到标记访问的数组,因此在中间需要有一个清零的过程
void initVisited()
{
    memset(MGraphVisited, 0, sizeof(MGraphVisited));
}
// 广搜
void BFS_MGraph(MGraph *graph, int v)
{
    int que[MaxNodeNum];                    		// 定义临时队列
    int rear = 0, front = 0; int cur;       		// 定义尾巴,头,要访问的当前顶点

    rear = (rear + 1) % MaxNodeNum;         		// 实现尾指针的循环移动,队尾先加
    que[rear] = v;									// 入队
    MGraphVisited[v] = 1;							// 入队之后就进行标记
    while (front != rear)
    {
        front = (front + 1) % MaxNodeNum;   		// 出队
        cur = que[front];                   		// 取出来

        visitMGraphNode(&graph->vex[cur]);			// 访问
        for (int i = 0; i < graph->nodeNum; ++i)	// 继续入队可遍历的节点
        {
            if (isEdge(graph->edges[cur][i]) && !MGraphVisited[i])
            {
                rear = (rear + 1) % MaxNodeNum;
                que[rear] = i;
                MGraphVisited[i] = 1;
            }
        }
    }
}

小结

本篇主要以邻接矩阵的实现为主,下篇将会迎来邻接表 哦~期待 i n g ing ing

希望各位不吝赐教~

相关推荐
xiaoye-duck3 小时前
数据结构之栈和队列-栈
数据结构
国服第二切图仔3 小时前
Rust开发之Trait作为参数与返回值使用
开发语言·后端·rust
山峰哥3 小时前
KingbaseES 表空间与模式优化策略深度研究报告
开发语言·数据结构·数据库·oracle·深度优先
AndrewHZ3 小时前
【图像处理基石】多波段图像融合算法入门:从概念到实践
图像处理·人工智能·算法·图像融合·遥感图像·多波段·变换域
yong99903 小时前
C++语法—类的声明和定义
开发语言·c++·算法
狂奔的sherry3 小时前
构造/析构/赋值运算理解
开发语言·c++
大佬,救命!!!3 小时前
C++多线程运行整理
开发语言·c++·算法·学习笔记·多线程·新手练习
合作小小程序员小小店4 小时前
web网页开发,旧版在线%考试,判题%系统demo,基于python+flask+随机分配考试题目,基于开发语言python,数据库mysql
开发语言·后端·python·mysql·flask·html5
蜗牛沐雨4 小时前
C++ 输出流(Output Stream)全解析
开发语言·c++