图的深度优先搜索和广度优先搜索

图的表示

数据结构中,图的最常见的两种表示方法是邻接矩阵表示法和邻接表表示法。
邻接矩阵用一个二维表来表示各个顶点之间的邻接关系(即是否有边相连),适合于稠密图且顶点数较少的图。一般建立在邻接矩阵表示的图的搜索的时间复杂度O(n2)

邻接表为每一个顶点建立一个链表,把与之邻接的顶点插入到以该顶点为头节点的链表中,适合于稀疏图的存储。一般建立在邻接表表示的图的搜索的时间复杂度为O(n+e)。

由于邻接矩阵结构简单,我主要复习邻接表的表示方法。

#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<stack>
#include<string.h>
using namespace std;
#define MAXNUM 200
#define VertexType int
typedef struct ArcNode {   //邻接点结构体
    int adjvex;//顶点的编号
    struct ArcNode * nextarc;
}ArcNode;
typedef struct VNode{   //表头结点结构体
    VertexType data;//顶点的名称
    struct ArcNode * firstarc;
}VNode,AdjList[MAXNUM];
typedef struct {       //邻接表结构体
    AdjList vercies;//邻接表数组
    int arcnum;//图的边数
    int vexnum;//图的顶点数
}AMGraph;

如何利用邻接表创建一个图?

bool visited[MAXNUM];
void create_AMGraph(AMGraph &G){      //引用 创建一个图
    cin>>G.vexnum>>G.arcnum;     //输入顶点个数和边的个数

    for(int i=0;i<G.vexnum;i++){   //初始化邻接表
        G.vercies[i].data = i;
        G.vercies[i].firstarc = NULL;
    }

    int x,y;
    for(int i=0;i<G.arcnum;i++){   //建立邻接表边关系 x->y   y->x
        cin>>x>>y;
        ArcNode * p = new ArcNode;
        p->adjvex = x;
        p->nextarc = G.vercies[y].firstarc;
        G.vercies[y].firstarc = p;

        ArcNode *q = new ArcNode;
        q->adjvex = y;
        q->nextarc = G.vercies[x].firstarc;
        G.vercies[x].firstarc = q;
    }
    return;
}

图的搜索

图的搜索也叫图的遍历,是从图的某一个顶点出发,按照某种方法对图中所有顶点访问且仅访问一次。图的遍历是图的最基本问题,是求解关键路径,图的连通性,拓扑排序等问题的基础。
根据图的搜索方向,图的搜索可以分为深度优先搜索和广度优先搜索。

深度优先搜索

图的深度优先搜索类似于树的先序遍历。
可以递归,也可以用栈,由于用到栈能够使算法优化。在这里只是文字叙述递归实现的步骤。

递归实现深度优先搜索:

  1. 从图中某个顶点v出发,访问v;
  2. 找出刚被访问顶点的第一个邻接点,并访问它。以该顶点作为新的扩展节点,重复上述步骤,直到刚访问过的顶点没有未被访问的邻接点为止;
  3. 返回前一个访问过的且仍有未被访问的邻接点的顶点,找到该顶点的下一个邻接点,访问它;
  4. 重复2和3,直到图中所有顶点都被访问为止。

利用栈实现图的深度优先搜索:
这里给出一篇文献:基于栈的非递归深度优先遍历算法的设计与实现
在这里插入图片描述

我的理解是:

首先初始化栈,初始化标记数组visited
然后初始节点v0入栈
while 栈非空时:
    栈顶元素出栈
    if 栈顶元素未被标记访问:
    	访问它并标记被访问
    	并且将与之邻接的未被访问的顶点全部入栈
    else 
    	continue;
end

我的代码是:

bool visited[MAXNUM];
void DFS(AMGraph G,int d){   //深度优先搜索
    stack<int>mys;memset(visited,false,sizeof(visited));
    mys.push(d);   //第一个节点进栈,当栈非空时
    while(!mys.empty()){  //当站栈非空的时候
        int x = mys.top();  //栈顶元素出栈
        mys.pop();
        if(!visited[x]){    //如果栈顶元素没有被访问过 先访问它 并且标记访问
            cout<<x<<endl;//访问这个顶点
            visited[x] = true;//标记这个顶点被访问过了
            for(ArcNode *p=G.vercies[x].firstarc;p!=NULL;p=p->nextarc){  //再遍历邻接顶点,未被访问的邻接点入栈
                if(!visited[p->adjvex])
                    mys.push(p->adjvex);
            }
        }
    }

}

一个小的测试样例:
在这里插入图片描述
在这里插入图片描述

广度优先搜索

广度优先搜索类似于树的层序遍历。
其文字描述为:

  1. 从图中某个顶点v出发,访问v;
  2. 依次访问v的各个未被访问的临界顶点;
  3. 分别从这些邻接点出发依次访问他们的邻接点,并且使得“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问。

广度优先需要用到队列,我的理解是:

初始化队列,初始化标记数数组
访问初始节点,标记被访问并入队
while 队列非空时:
	队首节点出队
	遍历队首节点的所有邻接点:
		if 队首节点的邻接点未被访问:
			访问它,标记访问,并入队
end

我的代码是:

bool visited[MAXNUM];
void BFS(AMGraph G,int d){
    queue<int>myq;memset(visited,false,sizeof(visited));
    cout<<d<<endl;  //先访问初始节点
    visited[d] = true; //标记初始节点已经被访问了
    myq.push(d);    //初始节点进队
    while(!myq.empty()){    //当队列非空的时候
        int x = myq.front();
        myq.pop();      //队首节点出队
        for(ArcNode *p = G.vercies[x].firstarc;p!=NULL;p=p->nextarc){  //当队列非空时
            if(!visited[p->adjvex]){    //依次遍历所有邻接点
                //若未被访问过,则访问这个点,并且标记访问过,并且入队
                cout<<p->adjvex<<endl;
                visited[p->adjvex] = true;
                myq.push(p->adjvex);
            }
        }
    }
}

测试样例:
在这里插入图片描述
在这里插入图片描述
所有代码如下:

#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<stack>
#include<queue>
#include<string.h>
using namespace std;
#define MAXNUM 200
typedef struct ArcNode {
    int adjvex;
    struct ArcNode * nextarc;
}ArcNode;
typedef struct VNode{
    int data;
    struct ArcNode * firstarc;
}VNode,AdjList[MAXNUM];
typedef struct {
    AdjList vercies;
    int arcnum;
    int vexnum;
}AMGraph;
void create_AMGraph(AMGraph &G){
    cin>>G.vexnum>>G.arcnum;     //输入顶点个数和边的个数

    for(int i=0;i<G.vexnum;i++){   //初始化邻接表
        G.vercies[i].data = i;
        G.vercies[i].firstarc = NULL;
    }

    int x,y;
    for(int i=0;i<G.arcnum;i++){   //建立邻接表 x-y   y-x
        cin>>x>>y;
        ArcNode * p = new ArcNode;
        p->adjvex = x;
        p->nextarc = G.vercies[y].firstarc;
        G.vercies[y].firstarc = p;

        ArcNode *q = new ArcNode;
        q->adjvex = y;
        q->nextarc = G.vercies[x].firstarc;
        G.vercies[x].firstarc = q;
    }
    return;
}
bool visited[MAXNUM];
void DFS(AMGraph G,int d){
    stack<int>mys;
    mys.push(d);
    while(!mys.empty()){  //当站栈非空的时候
        int x = mys.top();
        mys.pop();
        if(!visited[x]){
            cout<<x<<endl;//访问这个顶点
            visited[x] = true;//标记这个顶点被访问过了
            for(ArcNode *p=G.vercies[x].firstarc;p!=NULL;p=p->nextarc){
                if(!visited[p->adjvex])
                    mys.push(p->adjvex);
            }
        }
    }

}
void BFS(AMGraph G,int d){
    queue<int>myq;
    cout<<d<<endl;  //先访问初始节点
    visited[d] = true; //标记初始节点已经被访问了
    myq.push(d);    //初始节点进队
    while(!myq.empty()){    //当队列非空的时候
        int x = myq.front();
        myq.pop();      //队首节点出队
        for(ArcNode *p = G.vercies[x].firstarc;p!=NULL;p=p->nextarc){  //当队列非空时
            if(!visited[p->adjvex]){    //依次遍历所有邻接点
                //若未被访问过,则访问这个点,并且标记访问过,并且入队
                cout<<p->adjvex<<endl;
                visited[p->adjvex] = true;
                myq.push(p->adjvex);
            }
        }
    }
}
int main()
{
    AMGraph G;
    create_AMGraph(G);
    int d;cin>>d;
    for(int i=0;i<G.vexnum;i++){
       cout<<i<<":";
       for(ArcNode *p=G.vercies[i].firstarc;p!=NULL;p=p->nextarc){
            cout<<p->adjvex<<" ";
       }
       cout<<endl;
    }
    memset(visited,false,sizeof(visited));
    DFS(G,d);//BFS(G,d);
    return 0;
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值