图的深度/广度优先搜索(基于邻接矩阵存储结构)


前言

关于数据结构中图的 深度/广度 优先搜索(基于邻接矩阵存储结构)


一、图

图的定义

图(graph)是 n (n>=0) 个元素的有限集。图可以表示成二元组的形式,即:Graph= ( V , E ) 。其中,V是图中数据元素的集合,通常称为顶点集;E是数据元素之间关系的集合,通常称为边集。数据元素用顶点表示,数据元素之间的关系用边(无方向)或弧(有方向)表示。

在这里定义一个无向图G如下,方便后续推导:
V={A,B,C,D,E}
E={(A,B),(A,C),(B,D),(B,E),(C,E)}
在这里插入图片描述

图的存储结构(邻接矩阵)

图的邻接矩阵 (adjacent matrix) 表示法是用一个一维数组来存放顶点的信息,再用一个二维数组来存放边或弧的信息的表示方法,又称数组表示法。常采用矩阵的形式来描述边或弧的信息。

例 (G的邻接矩阵表示):

| 0 | 1 | 1 | 0 | 0 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 0 | 0 | 0 | 1 |
| 0 | 1 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 | 0 |

邻接矩阵类的定义如下(C++):

class _adjMatrix /*邻接矩阵存储结构类名*/
{
public:
    _adjMatrix() {};
    ~_adjMatrix() {};
    int vexnum, edgenum, kind;   /*顶点数、边(或弧)数及图的种类*/
    vector<char> vex;            /*存放顶点信息的一维数组*/
    vector<vector<int> >edge;    /*存边(或弧)的二维数组*/
  //void Creadjm();/*简单结构建立图的邻接矩阵*/
  //void list();/*输出函数*/
  //int Dfs(int now);/*depth-first search,深度优先搜索*/
  //void Bfs(int begin);/*breadth-first search,广度优先搜索*/
  //vector<int>visited;/*访问标记数组*/
};

邻接矩阵的构建函数:void Creadjm();/*简单结构建立图的邻接矩阵*/

void _adjMatrix::Creadjm() {
    int i, j, k;
    cout << "<Input vexnum ,kind and edgenum>" << endl;
    cout << "vertex_number: "; cin >> vexnum;
    if (vexnum > MAX)cout << "Your sizes out of range";
    cout << "kind(directed: 1, undirected: 0): "; cin >> kind;
    cout << "edges_number: "; cin >> edgenum;
    //判断边数是否合法,有向完全图边数是无向的两倍
    //n个顶点的完全图边数公式:edges=n(n-1)/2
    if (kind == 0 && edgenum > vexnum * (vexnum - 1) / 2)
       cout << "Your edges out of range";/*if待完善*/
    if (kind == 1 && edgenum > vexnum * (vexnum - 1))
       cout << "Your edges out of range";/*if待完善*/
    /*赋值代表顶点的字符,>='A',<='Z'*/
    for (k = 0; k < vexnum; ++k)vex.push_back('A' + k);
    visited.resize(vexnum);            /*初始化"访问标记"数组*/
    edge.resize(vexnum);               /*初始化邻接矩阵*/
    for (i = 0; i < vexnum; ++i)       /*初始化邻接矩阵*/
        edge[i].resize(vexnum);        /*初始化邻接矩阵*/
    /*输入每条边*/
    cout << "<Input " << edgenum << " edges : (instance: 1 2)>" << endl;
    int edges = edgenum;
    for (k = 0; k < edgenum; ++k) {
        cout << "(resiude: " << edges-- << " edges): ";
        cin >> i >> j;                   /*输入边或弧*/
        if (i >= vexnum || j >= vexnum)
           cout << "Error: out of range";/*if待完善*/
        edge[i][j] = 1;
        if (kind == 0)edge[j][i] = 1;    /*若是有向图,则取消该赋值语句*/
    }
}

运行,输入以下数据:
在这里插入图片描述

按上述步骤完成创建,系统会生成一个存放元素值的一维数组:

vex[]ABCDE

还有一个存放顶点关系的二维数组 edge[][] :

vex[] \ edge[][][0][1][2][3][4]
A01100
B10011
C10001
D01000
E01100

图的邻接矩阵存储就简单完成了。

二、图的遍历

简介:与线性表及树的遍历类似,图的遍历就是按照某种顺序依次访问图中的所有顶点,而且每个顶点仅访问一次。

深度优先搜索

深度优先搜索 (Depth first search) 是指在图中任选一个出发点v并访问;从顶点v的邻接点中按指定顺序选择一个未曾访问过的顶点w作为新的出发点,继续进行深度优先搜索;若w的所有邻接点均已经访问过,则回溯到前一个访问过的顶点继续进行深度优先搜索,直到图中所有和v有路径相通的顶点均被访问过为止。若此时图中仍有未访问的顶点,则另选一个尚未访问过的顶点作为新的出发点,重复上述过程,直到图中所有的顶点均已被访问为止。

模型图(G图为例):

1.从n号下标出发,访问vex[n];
2.依次循环edge[n][i++];
3.当循环遇到edge[0][i]的值为1的元素(且没有访问标记,即visited[i]=0),此时将元素下标i作为参数继续调用此函数,依次递归;
在这里插入图片描述

4.i>=vexnum时退出循环,等起点列的循环结束后,搜索visited数组,检查元素是否都被标记;
5.若存在visited[n]为0,则将n作为参数重复1~4的步骤。

C++代码实现:

/*Depth-first search,深度优先搜索 (基于邻接矩阵存储结构) */
int _adjMatrix::Dfs(int now) {  
    static int flag = 0;  //静态变量标记递归位置,0为起始位置
    cout << vex[now];     //访问
    visited[now] = 1;     //置访问标记
    for (int i = 0; i < vexnum; ++i) //循环访问当前(now)列的元素
        if (edge[now][i] == 1 && visited[i] == 0) {
            flag++;   //递归前取消起始位置(flag=0)的标记
            Dfs(i);   //递归搜索
        }
    if (flag != 0)return --flag;//若为起点则取消,继续搜索不连通的下一部分
                                //若不为起点则回溯到上一个访问过的顶点
    for (int i = 0; i < vexnum; ++i)       //搜索不连通的其他部分
        if (visited[i] == 0)Dfs(i);
}

广度优先搜索

广度优先搜索 (Breadth first search) 是指在图中任选一个出发点v,并访问之;接着依次访问顶点v的所有邻接点,然后再分别指定v的每个邻接点作为出发点继续进行广度优先搜索,依次类推,直到图中所有和v有路径相通的顶点都已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的出发点,重复上述过程,直到图中所有顶点均已被访问为止。

模型图(G图为例):
在这里插入图片描述
1.把初始元素入队
2.循环:
(1)判断队空,非空进入循环,队空循环结束
(2)队头元素n出队,访问 vex[n],并将edge[n][i++]中值为1(且入队标记数组中对应值为0,即visited[i]==0)的列下标入队,同时在入队标记数组中标记此下标。
3.在入队标记数组visited[]中搜索尚未被标记的下标,若存在则作为参数重复1~3步骤。

C++代码实现:

 /*Breadth-first search,广度优先搜索 (基于邻接矩阵存储结构) */
void _adjMatrix::Bfs(int begin) {  
    static deque<int>queue;
    queue.push_back(begin);              //初始顶点入队
    visited[begin] = 1;                  //置初始入队标记
    while (!queue.empty()) {             //队列不空
        cout << vex[queue.front()];      //访问
        int popnum = queue.front();      //记录即将出队的元素
        queue.pop_front();               //出队
        for (int i = 0; i < vexnum; ++i) //遍历第(出队元素值)列
        //若存在边(值为1)且无入队标记
            if (edge[popnum][i] == 1 && visited[i] == 0) { 
                queue.push_back(i);           //入队
                visited[i] = 1;               //置入队标记
            }
    }
    for (int i = 0; i < vexnum; ++i)  //搜索不连通的其他部分
        if (visited[i] == 0)Bfs(i);
}

其他

调试:
在这里插入图片描述
头文件:

#include <iostream>
#include<vector>
#include<deque>
#include<Windows.h>
using namespace std;
/*亮黄色*/ #define _yellowText SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED) /*亮黄色*/
/*正常白色*/ #define _white SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY)
#define MAX 10

测试函数:

int main()
{
    _adjMatrix G;
    _yellowText; cout << "<Create a graph> " << endl;_white;
    G.Creadjm();
    _yellowText; cout << "<Output adjacent matrix>" << endl; _white;
    G.list();
    _yellowText; cout << "<Depth-first search from zero>"<<endl; _white;
    G.Dfs(0); cout << endl << endl;
    G.visited.clear();
    G.visited.resize(G.vexnum);
    _yellowText; cout << "<Breadth-first search from zero>" << endl; _white;
    G.Bfs(0); cout << endl;
}

参考文献:
《数据结构(C语言版)(第3版)》:第6章 图

啊写完,没写容错
好累,不写了,光速逃离,咻 !~

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莉妮可丝的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值