图的DFS与BFS

图的DFS与BFS(C++)


概述

大一学生,作为我的第一篇Blog,准备记录一下图的基本操作:图的创建与遍历。请大佬多多包涵勿喷。
图可以采用邻接表,邻接矩阵,十字链表等多种储存结构进行储存,这里为了方便演示算法,采用邻接矩阵。 图为边的权值都默认为1的无向图 。
国内大学现行教材大多是C语言实现,然而C语言其实并不适合实现ADT,故这里使用和C语言相近的C++,引入OOP的机制进行类封装,更直观和容易理解。


代码

1.首先,因为要用到DFS,这里分别采用递归的方式和非递归方式(要用到STL提供的栈),故引入头文件,BFS利用队列,引用头文件queue。
接着对图的抽象数据类型进行声明,类的属性有两个,分别是储存顶点容器vertex和储存边的容器edge,提供3个接口,分别对应三种遍历方式。

#include<iostream>
#include<queue>
#include<vector>
#include<stack>
#include<mem.h>
int inf=-9999;
using namespace std;

//为了增强复用性,这里封装成类模板,虚拟类型为T
template <class T>
struct Graph{
	//存放顶点
    vector<T> vertex;
    //存放边
    vector<vector<int>> edge;
    //标记数组
    bool book[100];
    //构造函数
    Graph(int n,int m);
    //析构函数
    ~Graph();
    //递归深度优先遍历
    void DFS_recursion(int cur);
    //非递归深度优先遍历
    void DFS_stack(int cur);
    //广度优先遍历
    void BFS(int cur);	    
};

2.接着,定义Graph类的构造函数与析构函数,构造函数传入两个参数,对应图的定点数和边数,并创建一个book标记数组来记录定点是否访顶点,初始化book全部置为0,表示顶点都没访问。还有一点值得注意的是:这里为了方便,直接将顶点的数值设置为顶点的下标。

template <class T>
Graph<T>::Graph(int n,int m){
   //为了使顶点对应的下标符合人类思维(从1起),这里的容器大小分配为n+1;
    vertex.resize(n+1);
    edge.resize(n+1);
    for(int i=0;i<n+1;i++){
    edge[i].resize(n+1);
    }
    //初始化邻接矩阵
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(i==j) edge[i][j]=0;
            else edge[i][j]=inf;
        }
    }
    cout<<"请输入各条边的两个邻点"<<endl;
    for(int i=1;i<=m;i++){
        int a,b;
        cin>>a>>b;
        //这里使用无向图,故边应用edge[a][b]=edge[b][a]=1;初始化
        edge[a][b]=edge[b][a]=1;
    }
    ////因为各顶点的值为设为int类型,故为了方便,将各顶点的值初始化为其下标
    for(int i=1;i<=m;i++){
        vertex[i]=i;
    }
    //标记数组初始化
    memset(book,0,100);
}

template <class T>
Graph<T>:: ~Graph(){
    vertex.clear();
    edge.clear();
}

3.以下分别是3种不同的图的遍历方法的实现:

template <class T>
void Graph<T>::DFS_recursion(int cur){
    book[cur]=true;
    cout<<vertex[cur];
    for(int i=0;i<vertex.size();i++){
        if(edge[cur][i]==1&&book[i]==0){
            DFS_recursion(i);
        }
    }
}

template <class T>
void Graph<T>::DFS_stack(int cur){
    stack<int> s;
    s.push(cur);
    while(!s.empty()){
        int top=s.top();
        if(book[top]==0){
            cout<<top;
            book[top]=1;
        }
        else s.pop();
        for(int i=0;i<vertex.size();i++){
            if(book[i]==0&&edge[top][i]==1){
                s.push(i);
                break;
            }
        }
    }
    return;
}

template <class T>
void Graph<T>::BFS(int cur){
    queue<int> q;
    q.push(cur);
    book[cur]=1;  
    while(!q.empty()){
        int front=q.front();
        q.pop();
        cout<<vertex[front];
        for(int i=0;i<vertex.size();i++){
            if(edge[front][i]==1&&book[i]==0){
                q.push(i);
                book[i]=1;
            }
        }
    }
}

4.最后是测试部分:主函数创建实例,并调用类的方法完成对图的操作。这里初始化为5个节点5条边,权值都默认为1。调用时都从顶点1开始便遍历。注意:每次便利完成后都要用memset将book标记数组全部置0,表示没访问过。

int main(){
	//将类模板实例化为模板类,再将模板类实例化为一个对象
    Graph<T>* G=new Graph<int>(5,5);

    cout<<"深度优先遍历(递归)如下:"<<endl;
    G->DFS_recursion(1);
    memset(G->book,0,100);
    cout<<endl;

    cout<<"深度优先遍历(非递归)如下:"<<endl;
    G->DFS_stack(1);
    memset(G->book,0,100);
    cout<<endl;

    cout<<"广度优先队列遍历如下:"<<endl;
    G->BFS(1);
    cout<<endl;
     
    system("pause");
    return 0;
}

输出结果

测试控制台输出如下:
控制台输出

posted @ 2019-04-10 10:56  orion-orion  阅读(222)  评论(2编辑  收藏  举报