当前位置: 首页 > news >正文

最短路径——BFS

前面我们已经给大家讲过了图中的两个比较重要的遍历算法,分别是深度优先遍历和广度优先遍历,今天我们来学习一下具体应用,先给大家介绍一下在无权图中寻找单源最短路径

单源最短路径---顾名思义就是从一个源头出发,到某个顶点的最短路径

在这里我们可以使用之前讲过的广度优先算法来实现

但除此之外我们还需要两个数组分别是:

        1.d[VertexMax]----这个用来存储路径的长度

        2.path[VertexMax]----这个用来存储最短路径从哪个顶点过来

具体的代码如下:

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<limits.h>
#define VertexNum 5
//定义边
typedef struct Edge
{
int adjvex;//邻接顶点的索引
struct Edge* next;//下一个顶点的位置
}Edge;
//定义顶点
typedef struct VNode
{
char data;//顶点
struct Edge* first;//指向该顶点第一条边
}VNode;
//定义图
typedef struct array
{
VNode arr[VertexNum];//定义数组
int Vertex;//顶点和边的数量
}Graph;
//这样的话就是存在一个arr数组,数组里的每个元素都是VNode型的,每个元素里面有个顶点和顶点的第一条边的指针
//每一个指针里面又有下一个顶点在数组里的下标和地址

//带头结点的链式队列
typedef struct LinkNode
{
int data;//数据域
struct LinkNode* next;//指针域
}LinkNode;
//定义队列
typedef struct Queue
{
struct LinkNode* front, * rear;//定义头指针和尾指针
}Queue;
//初始化队列
void InitQueue(Queue* Q)
{
Q->front = Q->rear = (LinkNode*)malloc(sizeof(LinkNode));
if (Q->front == NULL)
return;//分配失败
Q->front->next = NULL;
return;
}
//结点入队
bool EnQueue(Queue* Q, int i)
{
if (Q->front == NULL)
return false;//队列不存在
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
//用尾插法
s->next = NULL;
s->data = i;
Q->rear->next = s;
Q->rear = s;
return true;
}
//结点出队
bool DeQueue(Queue* Q,int * i)
{
if (Q->front == NULL)
return false;//队列不存在
LinkNode* p = Q->front->next;
*i = p->data;
Q->front->next = p->next;
//如果rear是最后一个结点
if (p == Q->rear)
Q->rear = Q->front;
free(p);
return true;
}
//判断队列是否为空
bool EmptyQueue(Queue* Q)
{
//队列为空
if (Q->front == Q->rear && Q->front->next == NULL)
return true;
return false;
}
//初始化
void InitGraph(Graph* G)
{
//给每个数据域赋为0,指针域赋为空
int i = 0;
for (i; i < VertexNum; i++)
{
G->arr[i].data = '\0';
G->arr[i].first = NULL;
}
G->Vertex = 0;
}
//插入顶点
bool EnVertex(Graph* G)
{
if (G->Vertex >= VertexNum)//如果顶点的数量大于等于数组的长度
return false;
int i;
for (i = 0; i < VertexNum; i++)
{
G->arr[i].data = 'A' + i;
}
//顶点数量
G->Vertex = VertexNum;
return true;
}
//插入边
bool EnEdge(Graph* G)
{
if (G->Vertex <= 1)
return false;//1个顶点无需边
//为每个顶点的邻接顶点创建指针
int i;

    //A顶点
G->arr[0].first = (Edge*)malloc(sizeof(Edge));
if (G->arr[0].first == NULL)
return false;
Edge* p = G->arr[0].first;
p->adjvex = 1;
for (i = 2; i <= 3; i++)
{
Edge* s = (Edge*)malloc(sizeof(Edge));
if (s == NULL)
return false;
s->adjvex = i;
s->next = NULL;
p->next = s;
p = s;
}
//B顶点
G->arr[1].first = (Edge*)malloc(sizeof(Edge));
if (G->arr[1].first == NULL)
return false;
Edge* q = G->arr[1].first;
q->adjvex = 0;
q->next = (Edge*)malloc(sizeof(Edge));
if (q->next == NULL)
return false;
q = q->next;
q->adjvex = 3;//指向D
q->next = NULL;

    //C顶点
G->arr[2].first = (Edge*)malloc(sizeof(Edge));
if (G->arr[2].first == NULL)
return false;
Edge* C1 = G->arr[2].first;
C1->adjvex = 0;
C1->next = NULL;

    //D顶点
G->arr[3].first = (Edge*)malloc(sizeof(Edge));
if (G->arr[3].first == NULL)
return false;
Edge* D1 = G->arr[3].first;
//D的邻接顶点A
D1->adjvex = 0;
//D的邻接顶点B
D1->next = (Edge*)malloc(sizeof(Edge));
if (D1->next == NULL)
return false;//分配失败
D1 = D1->next;
D1->adjvex = 1;
D1->next = (Edge*)malloc(sizeof(Edge));
if (D1->next == NULL)
return false;//分配失败
D1 = D1->next;
D1->adjvex = 4;
D1->next = NULL;


//E顶点
G->arr[4].first = (Edge*)malloc(sizeof(Edge));
if (G->arr[4].first == NULL)
return false;
Edge* E1 = G->arr[4].first;
//E的邻接顶点D
E1->adjvex = 3;
E1->next = NULL;

    return true;
}
//求图中顶点x的第一个邻接点
// 若有则返回顶点号,若x没有邻接点或图中不存在x,则返回-1
int FirstNeighbor(Graph* G, char x)
{
if (G->Vertex == 0)
return INT_MIN;//非法图
if (G->Vertex == 1)
return INT_MIN;//一个顶点
//找到顶点的下标
int index = -1;
int i;
for (i = 0; i < VertexNum; i++)
if (G->arr[i].data == x)
{
index = i;
break;
}
if (index == -1)
return -1;//该顶点不存在
if (G->arr[index].first == NULL)
return -1;//该顶点没有邻接点
return G->arr[index].first->adjvex;//返回第一个邻接点
}
//假设图G中顶点y是顶点x的一个邻接点
// 返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1
int NextNeighbor(Graph* G, char x, char y)
{
if (G->Vertex == 0)
return INT_MIN;//非法图
if (G->Vertex == 1)
return INT_MIN;//只有一个顶点
//找到x的下标
int index = -1;
int i = 0;
for (i = 0; i < VertexNum; i++)
{
if (G->arr[i].data == x)
{
index = i;
break;
}
}

    if (index == -1)
return INT_MIN;//没有该顶点
//寻找y
Edge* p = G->arr[index].first;
while (p != NULL)
{
if (G->arr[p->adjvex].data == y)//找到了y
{
if (p->next == NULL)//没有邻接点
return -1;
return p->next->adjvex;//返回y的下一个元素的下标
}
p = p->next;
}
return INT_MIN;
}
//图的广度优先遍历
//访问结点
void visit(Graph* G, int i)
{
printf("%c", G->arr[i].data);
return;
}

//定义全局变量
bool flag[VertexNum]; //访问标记
int d[VertexNum]; //源点到其他顶点的路径
int path[VertexNum];//最短路径从哪个顶点过来
//真正的广度优先遍历算法
bool BFS(Graph* G, Queue* Q, int i)
{
//访问第一个遍历的顶点
visit(G, i);
//对第一个顶点进行初始操作
flag[i] = true;
d[i] = 0;//从源点到本身路径距离为0
path[i] = -1;//规定顶点不能指向自己

    //顶点入队
EnQueue(Q, i);
while (EmptyQueue(Q) != true)
{
DeQueue(Q,&i);//Q出队
//求图中第一个邻接点
int w = FirstNeighbor(G, G->arr[i].data);
for (w; w >= 0; w = NextNeighbor(G, G->arr[i].data, G->arr[w].data))
{
//未访问的顶点
if (flag[w] == false)
{
visit(G, w);
d[w] = d[i] + 1;
path[w] = i;
flag[w] = true;
EnQueue(Q, w);
}
}
}
return true;
}
//准备广度优先遍历
void BFST(Graph* G, Queue* Q)
{

    //把每一个标记赋值为false
int i;
for (i = 0; i < VertexNum; i++)
flag[i] = false;
//最短路径初始化为最大
for (i = 0; i < VertexNum; i++)
d[i] = INT_MAX;
//全部初始化为-1
for (i = 0; i < VertexNum; i++)
path[i] = -1;
//初始化队列
InitQueue(Q);
//每个连通分量遍历一次
for (i = 0; i < VertexNum; i++)
{
if (flag[i] == false)
BFS(G, Q, i);//从i开始
}

}
void ShortestPath(Graph * G,int d[],int path[],char ch1,char ch2)
{
printf("\n");
int i;
int index = -1; // 求出ch2的下标
for (i = 0; i < G->Vertex; i++)
if (G->arr[i].data == ch2)
index = i;
printf("从%c到%c的最短距离为%d\n", ch1, ch2, d[index]);
int temp = index;
int j = 0;
int tem[VertexNum];
while (path[temp] != -1)
{
tem[j] = temp;
temp = path[temp];
j++;
}
printf("%c", ch1);
while (j > 0)
{
printf("->%c",G->arr[tem[j - 1]].data);
j--;
}
printf("\n");

}
int main()
{
Graph G;//定义图
InitGraph(&G);//初始化
EnVertex(&G);//插入结点
EnEdge(&G);//插入边

    Queue Q;//定义队列
BFST(&G, &Q);//广度优先遍历

    //求从源顶点到另一个顶点的最短路径
ShortestPath(&G,d, path,'A','E');
return 0;
}
这个代码总体上来说跟广度优先算法是类似的,不熟悉的可以去观看我们之前的文章,但是这个代码也存在弊端,就是必须是无权图才可以。还有一种可以计算带权的算法,Dijkstra算法----这个算法在数据结构中非常重要,下次我将给大家进行讲解。

http://www.dtcms.com/a/537439.html

相关文章:

  • git“约定式提交” (Conventional Commits) 的规范
  • 上海做家教网站有哪些wordpress导航菜单制作
  • 【FPGA】时序逻辑计数器设计仿真验证
  • 【Camunda】工作流
  • 泸州市建设职工培训中心网站怎么建立本地网站
  • gunicorn和docker冲突吗
  • 学做网站多少钱青岛大型网站建设
  • 139.MIG DDR数据位宽选择72bit,带ecc时dm管脚会消失
  • 【Rust编程:从新手到大师】 Rust 数据类型全解析
  • C++十大排序算法
  • 公司网站维护由那个部门做百度竞价点击工具
  • Vue2 elementUI年份区间选择组件
  • 工装设计方案网站wordpress的仪表盘进不去
  • 深度学习笔记40-CGAN|生成手势图像
  • 浙江建设职业技术学院oa网站怎么做微信推广和宣传
  • React 08
  • 企业信息门户网站建设方案设计素材的网站
  • 如何将自己做的网站变成中文帮忙制作网页的公司
  • gpu driven:vello新执行流程
  • LangGraph的Agent长短时记忆的原理有什么区别,分别适用于什么业务场景
  • 定制网站开发的目的是什么做单位网站的公司吗
  • 做网站建立数据库自适应的网站模板
  • 路由硬盘做网站空间不中国城乡建中国城乡建设部网站
  • 电脑怎么做服务器 网站wordpress手机号网站
  • 跨境电商技术与运营双升级!亚马逊 / TikTok/Temu 本周新政解读,附卖家技术适配指南​
  • C++ 类的学习(七) 类的转换 和 嵌套类
  • C++进阶: 虚函数1-----继承中的灵魂
  • 软件协议使用应知应会
  • C语言进阶:深入探讨指针(一)
  • 网站备案 信息wordpress支付接口同步回调