欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-04 23:01:28
...


前言

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


一、图

图的定义

图(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)}
图的深度/广度优先搜索C++实现(基于邻接矩阵存储结构)

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

图的邻接矩阵 (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;    /*若是有向图,则取消该赋值语句*/
    }
}

运行,输入以下数据:
图的深度/广度优先搜索C++实现(基于邻接矩阵存储结构)

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

vex[] A B C D E

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

vex[] \ edge[][] [0] [1] [2] [3] [4]
A 0 1 1 0 0
B 1 0 0 1 1
C 1 0 0 0 1
D 0 1 0 0 0
E 0 1 1 0 0

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

二、图的遍历

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

深度优先搜索

深度优先搜索 (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作为参数继续调用此函数,依次递归;
图的深度/广度优先搜索C++实现(基于邻接矩阵存储结构)

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图为例):
图的深度/广度优先搜索C++实现(基于邻接矩阵存储结构)
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);
}

其他

调试:
图的深度/广度优先搜索C++实现(基于邻接矩阵存储结构)
头文件:

#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章 图

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