第六章 图 —— 邻接矩阵/邻接表构造图实现BFS/DFS/Dijkstra算法

第六章 图 —— 邻接矩阵/邻接表构造图实现BFS/DFS/Dijkstra算法

要求:
(1)采用邻接矩阵/邻接表建立图;
(2)采用深度优先/广度优先搜索方式遍历图;
(3)编程实现Dijkstra最短路径算法。

此处均以无向图为例

邻接矩阵:

在这里插入图片描述代码如下:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<bits/stdc++.h>
#define MaxVerNum 100 //顶点最大数目值
#define VexType char //顶点数据类型
#define EdgeType int //边数据类型,无向图时邻接矩阵对称,有权值时表示权值,没有时1连0不连
#define INF 0x3f3f3f3f//作为最大值
using namespace std;
//图的数据结构
typedef struct Graph
{
    VexType Vex[MaxVerNum];//顶点表
    EdgeType Edge[MaxVerNum][MaxVerNum];//边表
    int vexnum, arcnum;//顶点数、边数
} Graph;
//迪杰斯特拉算法全局变量
bool S[MaxVerNum]; //顶点集
int D[MaxVerNum];  //到各个顶点的最短路径
int Pr[MaxVerNum]; //记录前驱

//初始化函数
void InitGraph(Graph& G)
{
    memset(G.Vex, '#', sizeof(G.Vex));//初始化顶点表
    //初始化边表
    for (int i = 0; i < MaxVerNum; i++)
        for (int j = 0; j < MaxVerNum; j++)
            G.Edge[i][j] = INF;
    G.arcnum = G.vexnum = 0;          //初始化顶点数、边数
}

//插入顶点函数
bool InsertNode(Graph& G, VexType v)
{
    if (G.vexnum < MaxVerNum)
    {
        G.Vex[G.vexnum++] = v;
        return true;
    }
    return false;
}

//插入边函数
bool InsertEdge(Graph& G, VexType v, VexType w, int weight)
{
    int p1, p2;//v,w两点下标
    p1 = p2 = -1;//初始化
    for (int i = 0; i < G.vexnum; i++)//寻找顶点下标
    {
        if (G.Vex[i] == v)
            p1 = i;
        if (G.Vex[i] == w)
            p2 = i;
    }
    if (-1 != p1 && -1 != p2)//两点均可在图中找到
    {
        G.Edge[p1][p2] = G.Edge[p2][p1] = weight;//无向图邻接矩阵对称
        G.arcnum++;
        return true;
    }
    return false;
}

//判断是否存在边(v,w)函数
bool Adjancent(Graph G, VexType v, VexType w)
{
    int p1, p2;//v,w两点下标
    p1 = p2 = -1;//初始化
    for (int i = 0; i < G.vexnum; i++)//寻找顶点下标
    {
        if (G.Vex[i] == v)
            p1 = i;
        if (G.Vex[i] == w)
            p2 = i;
    }
    if (-1 != p1 && -1 != p2)//两点均可在图中找到
    {
        if (G.Edge[p1][p2] == 1)//存在边
        {
            return true;
        }
        return false;
    }
    return false;
}

bool visited[MaxVerNum];//访问标记数组,用于遍历时的标记

//广度遍历函数
void BFS(Graph G, int start)
{
    queue<int> Q;//辅助队列
    cout << G.Vex[start];//访问结点
    visited[start] = true;
    Q.push(start);//入队
    while (!Q.empty())//队列非空
    {
        int v = Q.front();//得到队头元素
        Q.pop();//出队
        for (int j = 0; j < G.vexnum; j++)//邻接点
        {
            if (G.Edge[v][j] < INF && !visited[j])//是邻接点且未访问
            {
                cout << "->";
                cout << G.Vex[j];//访问结点
                visited[j] = true;
                Q.push(j);//入队
            }
        }
    }//while
    cout << endl;
}

//深度遍历函数(递归形式)
void DFS(Graph G, int start)
{
    cout << G.Vex[start];//访问
    visited[start] = true;
    for (int j = 0; j < G.vexnum; j++)
    {
        if (G.Edge[start][j] < INF && !visited[j])//是邻接点且未访问
        {
            cout << "->";
            DFS(G, j);//递归深度遍历
        }
    }
}

//最短路径 - Dijkstra算法 参数:图G、源点v
void Dijkstra(Graph G, int v)
{
    //初始化
    int n = G.vexnum;//n为图的顶点个数
    for (int i = 0; i < n; i++)
    {
        S[i] = false;
        D[i] = G.Edge[v][i];
        if (D[i] < INF)
            Pr[i] = v; //v与i连接,v为前驱
        else
            Pr[i] = -1;
    }
    S[v] = true;
    D[v] = 0;
    //初始化结束,求最短路径,并加入S集
    for (int i = 1; i < n; i++)
    {
        int min = INF;
        int temp;
        for (int w = 0; w < n; w++)
            if (!S[w] && D[w] < min) //某点temp未加入s集,且为当前最短路径
            {
                temp = w;
                min = D[w];
            }
        S[temp] = true;
        //更新从源点出发至其余点的最短路径 通过temp
        for (int w = 0; w < n; w++)
            if (!S[w] && D[temp] + G.Edge[temp][w] < D[w])
            {
                D[w] = D[temp] + G.Edge[temp][w];
                Pr[w] = temp;
            }
    }
}

//输出最短路径
void Path(Graph G, int v)
{
    if (Pr[v] == -1)
        return;
    Path(G, Pr[v]);
    cout << G.Vex[Pr[v]] << "->";
}

//打印图的顶点表
void PrintVex(Graph G)
{
    cout<<"图中的顶点为:";
    for (int i = 0; i < G.vexnum; i++)
    {
        cout << G.Vex[i] << " ";
    }
    cout << endl;
}

//打印图的边矩阵
void PrintEdge(Graph G)
{
    cout<<"邻接矩阵为:"<<endl;
    for (int i = 0; i < G.vexnum; i++)
    {
        for (int j = 0; j < G.vexnum; j++)
        {
            if (G.Edge[i][j] == INF)
                cout << "∞ ";
            else
                cout << G.Edge[i][j] << " ";
        }
        cout << endl;
    }
}

//创建图功能实现函数
void CreateGraph(Graph& G)
{
    VexType v, w;
    int vn, an;//顶点数,边数
    cout << "请输入顶点数目:" << endl;
    cin >> vn;
    cout << "请输入边数目:" << endl;
    cin >> an;
    cout << "请输入所有顶点名称:" << endl;
    for (int i = 0; i < vn; i++)
    {
        cin >> v;
        if (InsertNode(G, v))
            continue;//插入点
        else
        {
            cout << "输入错误!" << endl;
            break;
        }
    }
    cout << "请输入所有边(每行输入边连接的两个顶点及权值):" << endl;
    for (int j = 0; j < an; j++)
    {
        int weight;
        cin >> v >> w >> weight;
        if (InsertEdge(G, v, w, weight))
            continue;//插入边
        else
        {
            cout << "输入错误!" << endl;
            break;
        }
    }
    PrintVex(G);
    cout<<endl;
    PrintEdge(G);
}

//广度遍历功能实现函数
void BFSTraverse(Graph G)
{
    for (int i = 0; i < MaxVerNum; i++)//初始化访问标记数组
    {
        visited[i] = false;
    }
    for (int i = 0; i < G.vexnum; i++)//对每个连通分量进行遍历
    {
        if (!visited[i])
            BFS(G, i);
    }
}

//深度遍历功能实现函数
void DFSTraverse(Graph G)
{
    for (int i = 0; i < MaxVerNum; i++)//初始化访问标记数组
    {
        visited[i] = false;
    }
    for (int i = 0; i < G.vexnum; i++)//对每个连通分量进行遍历
    {
        if (!visited[i])
        {
            DFS(G, i);
            cout << endl;
        }
    }
}

//调用最短路径-Dijkstra算法
void Shortest_Dijkstra(Graph& G)
{
    char vname;
    int v = -1;
    cout << "请输入源点名称:" << endl;
    cin >> vname;
    for (int i = 0; i < G.vexnum; i++)
    {
        if (G.Vex[i] == vname)
            v = i;
    }

    if (v == -1)
    {
        cout << "没有找到输入点!" << endl;
        return;
    }
    Dijkstra(G, v);
    cout << "目标点" << "\t" << "最短路径值" << "\t" << "最短路径" << endl;
    for (int i = 0; i < G.vexnum; i++)
    {
        if (i != v)
        {
            cout << "  " << G.Vex[i] << "\t" << "        " << D[i] << "\t";
            Path(G, i);
            cout << G.Vex[i] << endl;
        }
    }
}

//菜单
void menu()
{
    cout<<"1.创建图" << endl;
    cout<<"2.广度遍历"<<endl;
    cout<<"3.深度遍历" << endl;
    cout<<"4.迪杰斯特拉"<<endl;
    cout<<"5.退出" << endl;
}

//主函数
int main()
{
    int choice = 0;
    Graph G;
    InitGraph(G);
    while (1)
    {
        menu();
        cout<<"请输入菜单序号: "<<endl;
        cin>>choice;
        if (5 == choice)
            break;
        switch (choice)
        {
        case 1:
            CreateGraph(G);
            break;
        case 2:
            BFSTraverse(G);
            break;
        case 3:
            DFSTraverse(G);
            break;
        case 4:
            Shortest_Dijkstra(G);
            break;
        default:
            printf("输入错误!!!\n");
            break;
        }
        cout<<endl;
    }
    return 0;
}

邻接表

在这里插入图片描述
代码如下:

#include <bits/stdc++.h>
using namespace std;
#define MAXSIZE 100
int v[MAXSIZE];//标志位,判断该结点有没有被访问

typedef struct ArcNode //定义边节点
{
    int adjvex;//该节点在图中的位置
    int weight;//权值
    struct ArcNode *next;//指向下一条边的指针
} ArcNode;

typedef struct VNode //定义顶点信息
{
    char data;
    ArcNode *firstarc;//指向第一条依附于顶点的边的指针
} VNode,AdjList[MAXSIZE];//AdjList表示邻接表类型

typedef struct //定义邻接表
{
    int vexnum,arcnum;//顶点数和边数
    AdjList vertices;
} ALGraph;

//定位函数(将点的名称转化为点的位置)
int LocateVex(ALGraph G,char u)
{
    int i;
    for(i=1; i<=G.vexnum; ++i)
        if(u==G.vertices[i].data)
            return i;
    return -1;
}


void CreatUDG(ALGraph &G)//构建无向图
{
    int i,n,m,p1,p2;
    char a,b;
    ArcNode *s;
    cout<<"请输入顶点数和边数:"<<endl;
    cin>>n>>m;
    G.vexnum=n;
    G.arcnum=m;
    cout<<"输入顶点名称:"<<endl;
    for(i=1; i<=G.vexnum; i++)
    {
        cin>>G.vertices[i].data;
        G.vertices[i].firstarc=NULL;//一开始都指向NULL
    }
    cout<<"输入一条边对应的两个顶点:"<<endl;
    for(i=0; i<G.arcnum; i++)
    {
        cin>>a>>b;
        p1 = LocateVex(G,a);
        p2 = LocateVex(G,b);//将名称转化为位置
        s=new ArcNode[sizeof(ArcNode)];//生成一个新的边结点
        s->adjvex=p2;//邻接点的序号为p2
        s->next=G.vertices[p1].firstarc;
        G.vertices[p1].firstarc=s;//将新结点前插至边表头部

        s=new ArcNode[sizeof(ArcNode)];
        s->adjvex=p1;
        s->next=G.vertices[p2].firstarc;
        G.vertices[p2].firstarc=s;//同上,将新结点前插至边表头部
    }
}

int FirstAdjVex(ALGraph G,int k)//返回图G中与k相邻且未被访问过的点的编号,如果不存在这样的点返回-1
{
    ArcNode *s;
    s=G.vertices[k].firstarc;
    if(!s)
        return -1;
    while(s&&v[s->adjvex])//如果不空又访问过,就看下一个
        s=s->next;
    if(s)//不空说明找到了
        return s->adjvex;
    else//否则返回-1
        return -1;
}

int NextAdjVex(ALGraph G,int k,int w)//返回图G中与k相邻、除了编号是w之外的未被访问过的点的编号,如果不存在这样的点返回-1
{
    ArcNode *s;
    s=G.vertices[k].firstarc;
    if(!s)
        return -1;
    while(s&&(v[s->adjvex]||s->adjvex==w))//不空,并且要么是访问过,要么是w,这样的点我们统统忽略
        s=s->next;
    if(s)//不空说明找到了
        return s->adjvex;
    else//否则-1
        return -1;
}

void DFS(ALGraph G,int k)//对节点k进行深度优先遍历
{
    int w;
    v[k]=1;//进来就置1
    cout<<G.vertices[k].data<<" ";//遍历输出
    for(w=FirstAdjVex(G,k); w!=-1; w=NextAdjVex(G,k,w)) //找到所有和k相邻的节点的编号,挨个深搜递归
        if(!v[w])
            DFS(G,w);
}

void DFSTraverse(ALGraph G)//对所有点进行深度优先遍历(防止在非连通图中遗漏点)
{
    int i;
    memset(v,0,sizeof(v));
    for(i=1; i<=G.vexnum; i++)
        if(!v[i])
            DFS(G,i);
}

void BFS(ALGraph G)//广度优先遍历
{
    int i,k,w;
    queue<char> q;//定义一个队列存储点的名称
    memset(v,0,sizeof(v));//因为之前深搜过,这里重新置0一下
    for(i=1; i<=G.vexnum; i++) //依次对每个顶点进行考察(此处相当于把BFS和BFSTraverse合并)
        if(!v[i])//只要不空
        {
            v[i]=1;//只要不空,进来第一件事就是置1
            cout<<G.vertices[i].data<<" ";//置1之后马上输出
            q.push(G.vertices[i].data);//输出完了之后就入队
            while(!q.empty())//只要队列不空
            {
                k=LocateVex(G,q.front());//取队首元素
                q.pop();//然后队首出队
                for(w=FirstAdjVex(G,k); w!=-1; w=NextAdjVex(G,k,w)) //将所有和队首邻接的顶点依次入队,入一个,输出一个
                    if(!v[w])
                    {
                        v[w]=1;
                        cout<<G.vertices[w].data<<" ";
                        q.push(G.vertices[w].data);
                    }
            }
        }
}

int main()
{
    ALGraph G;
    CreatUDG(G);
    cout<<"DFS:";
    DFSTraverse(G);
    cout<<endl;
    cout<<"BFS:";
    BFS(G);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值