1.用BFS实现无权值单源最短路径问题
/*
在网络中,求两个不同顶点之间的所有路径中,边的权值之和最小的那一条路径。
Shortest Path
Source -----》 Destination
问题分类:
1.单源最短路径问题:从某固定源点触发,求其到所有其他顶点的最短路径。
1.1 无权图
1.2 有权图
2.多源最短路径问题:求任意两顶点间的最短路径
*/
//无权图的单源最短路算法
//邻接表
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
#define INIFITY 65536
typedef struct LNode_Vertex* Vertex;
typedef struct LNode_Edge* Edge;
typedef struct LNode_List* List;
typedef struct LNode_Queue* Queue;
struct LNode_Vertex
{
int vex;
Edge ptr_edge;
};
struct LNode_Edge
{
int adjvex;
int weight;
Edge next;
};
//因为邻接表的数据结构是,每个顶点表中的节点都引导出一串边节点
struct LNode_Queue
{
List front;
List rear;
};
struct LNode_List
{
int data;
List next;
};
LNode_Vertex ver[MAXSIZE];
int num_vertex, num_edge;
int visited[MAXSIZE];
int dist[MAXSIZE];//dist[w] = s到w的最短距离
int path[MAXSIZE];//path[w] = S到W的路上经过的某个顶点
void Create_Graph()
{
printf("你要输入的点和边数\n");
scanf("%d %d", &num_vertex, &num_edge);
printf("下面请输入顶点\n");
for (int i = 0; i < num_vertex; i++)
{
scanf("%d", &ver[i].vex);
}
printf("下面请输入顶点1,顶点2,权值\n");
for (int i = 0; i < num_edge; i++)
{
int vex1, vex2, w;
scanf("%d %d %d", &vex1, &vex2, &w);
Edge E = (Edge)malloc(sizeof(struct LNode_Edge));//建立一个边节点
if (ver[vex1].ptr_edge == NULL)
ver[vex1].ptr_edge = E;
else
{
Edge p = ver[vex1].ptr_edge;
while (p->next != NULL)//直到找到顶点节点所引导的一系列节点的最后
{
p = p->next;//每次往后移动一位
}
p->next = E;//将最后一个的next指向E
}
E->weight = w;
E->adjvex = vex2;
E->next = NULL;
}
}
void Print_Graph()
{
for (int i = 0; i < num_vertex; i++)
{
printf("%d——》", ver[i].vex);
if (ver[i].ptr_edge != NULL)
{
printf("[%d,%d]->", ver[i].ptr_edge->adjvex, ver[i].ptr_edge->weight);
while (ver[i].ptr_edge->next != NULL)
{
printf("[%d,%d]->", ver[i].ptr_edge->next->adjvex, ver[i].ptr_edge->next->weight);
ver[i].ptr_edge->next = ver[i].ptr_edge->next->next;
}
}
putchar('\n');
}
}
//为了完成BFS,首先我们需要完成一些关于队列的函数,包括入队和出队
Queue CreateQueue()
{
Queue Q = (Queue)malloc(sizeof(struct LNode_Queue));
Q->front = Q->rear = NULL;
return Q;
}
int Is_Empty(Queue Q)
{
if ((Q->front == NULL) && (Q->rear == NULL))
return 1;
else
return 0;
}
void AddQ(Queue Q, int Item)
{
List L = (List)malloc(sizeof(struct LNode_List));
if ((Q->rear == NULL))
{
Q->front = L;
Q->rear = L;
L->data = Item;
L->next = NULL;
}
else
{
Q->rear->next = L;
Q->rear = L;
L->data = Item;
L->next = NULL;
}
}
int ExitQ(Queue Q)
{
List q;
if (Q->front == NULL)
{
printf("Error:队列中没有元素可以出队");
return -1;
}
q = Q->front;
if (Q->front == Q->rear)
{
Q->front = NULL;
Q->rear = NULL;
}
else
Q->front = Q->front->next;
int ans = q->data;
free(q);
return ans;
}
void BFS(int S)
{
for (int j = 0; j < MAXSIZE; j++)
{
dist[j] = -1;
path[j] = -1;
}
dist[S] = 0;
Queue Q = CreateQueue();
AddQ(Q, S);
printf("*%d ", S);
while (!Is_Empty(Q))
{
int V = ExitQ(Q);
//j的所有邻边全部入队
Edge E = ver[V].ptr_edge;//暂时不去考虑没有邻边的情况
do
{
int W = E->adjvex;
if (dist[W] == -1)//如果没被访问过
{
dist[W] = dist[V] + 1;//j向外扩张一圈的那一圈元素中有k,所有必然距离+1
path[W] = V;//前一个顶点是必须要经过的顶点,可以用于打印路径
printf("*%d ", W);
AddQ(Q, W);
}
E = E->next;
} while (E != NULL);
}
for (int i = MAXSIZE -1; i >= 0; i--)
if(path[i] != -1)
printf("%d ", path[i]);
}
int main()
{
Create_Graph();
Print_Graph();
putchar('\n');
BFS(3);
return 0;
}
/*
4 6
0 1 2 3
0 3 9
3 2 8
0 1 5
1 3 6
0 2 7
2 0 2
*/
2.Dijkstra算法下的单源有权值最短路算法
//邻接表
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100
#define INIFITY 655535
typedef struct LNode_Vertex* Vertex;
typedef struct LNode_Edge* Edge;
typedef struct LNode_List* List;
typedef struct LNode_Queue* Queue;
struct LNode_Vertex
{
int vex;
Edge ptr_edge;
};
struct LNode_Edge
{
int adjvex;
int weight;
Edge next;
};
//因为邻接表的数据结构是,每个顶点表中的节点都引导出一串边节点
struct LNode_Queue
{
List front;
List rear;
};
struct LNode_List
{
int data;
List next;
};
LNode_Vertex ver[MAXSIZE];
int num_vertex, num_edge;
int visited[MAXSIZE];
int dist[MAXSIZE];
int path[MAXSIZE];
int collected[MAXSIZE];
void Create_Graph()
{
printf("你要输入的点和边数\n");
scanf("%d %d", &num_vertex, &num_edge);
printf("下面请输入顶点\n");
for (int i = 0; i < num_vertex; i++)
{
scanf("%d", &ver[i].vex);
}
printf("下面请输入顶点1,顶点2,权值\n");
for (int i = 0; i < num_edge; i++)
{
int vex1, vex2, w;
scanf("%d %d %d", &vex1, &vex2, &w);
Edge E = (Edge)malloc(sizeof(struct LNode_Edge));//建立一个边节点
if(ver[vex1-1].ptr_edge == NULL)
ver[vex1-1].ptr_edge = E;
else
{
Edge p = ver[vex1-1].ptr_edge;
while (p->next != NULL)//直到找到顶点节点所引导的一系列节点的最后
{
p = p->next;//每次往后移动一位
}
p->next = E;//将最后一个的next指向E
}
E->weight = w;
E->adjvex = vex2;
E->next = NULL;
}
}
void Print_Graph()
{
for (int i = 0; i < num_vertex; i++)
{
printf("%d——》", ver[i].vex);
if (ver[i].ptr_edge != NULL)
{
printf("[%d,%d]->", ver[i].ptr_edge->adjvex, ver[i].ptr_edge->weight);
while (ver[i].ptr_edge->next != NULL)
{
printf("[%d,%d]->", ver[i].ptr_edge->next->adjvex, ver[i].ptr_edge->next->weight);
ver[i].ptr_edge->next = ver[i].ptr_edge->next->next;
}
}
putchar('\n');
}
}
void Dijkstra(int s)
{
int MinDist = INIFITY;
int min_i;
int i;
for (i = num_vertex; i >= 0; i--)//先粗略初始化,把距离权值设为无穷,把经过路径点设置为-1
{
dist[i] = INIFITY;
path[i] = -1;
collected[i] = 0;//收集到为1,未收集到为0
}
dist[s] = 0;//自己到自己的距离为0
//将源点的邻接点距离设置为权值。
Edge E = ver[s].ptr_edge;
while (E != NULL)
{
dist[E->adjvex] = E->weight;//到邻节点的距离就是权值
E = E->next;
}
while (1)
{
MinDist = INIFITY;
for (i = 0; i < num_vertex; i++)//找到dist中最小的那个值
{
if(collected[i] == 0)//我要在未搜集的那些里面去寻找最小距离
if (MinDist > dist[i])
{
MinDist = dist[i];
min_i = i;//并且保存最小值的下标
}
}
if (MinDist == INIFITY)//如果MinDist未发生改变,说明所有点都被搜集到了
break;
collected[min_i] = 1;//最小距离点被访问到了,下面应该去找这个最小值的邻接点了
Edge E = ver[min_i].ptr_edge;
while (E != NULL)
{
if (!collected[E->adjvex])//如果有没被访问过的邻接点
if ((dist[min_i] + E->weight) < dist[E->adjvex])//如果我走两段的距离比直接走要小,那么就取代它
{
dist[E->adjvex] = dist[min_i] + E->weight;
path[E->adjvex] = min_i;//说明我是通过min_走到的邻接点
}
E = E->next;
}
}
//以上就是在完成初始化
}
int main()
{
Create_Graph();
Print_Graph();
putchar('\n');
return 0;
}
/*
4 6
1 2 3 4
1 4 9
4 3 8
1 2 5
2 4 6
1 3 7
3 1 2
*/
3. Floyd算法下多源最短路径
/*
算法描述
1)算法思想原理:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。
从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)
从任意节点i到任意节点j的最短路径不外乎2种可能,1是直接从i到j,2是从i经过若干个节点k到j。
所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,
我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,
我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
2).算法描述:
a.从任意一条单边路径开始。所有两点之间的距离是边的权,如果两点之间没有边相连,则权为无穷大。
b.对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。
*/
/* 邻接矩阵表示的图结构 */
#include <stdio.h>
#include <stdlib.h>
typedef int VertexType;
typedef int EdgeType;
#define MAXVEX 100
#define INFINITY 65535
#define DEBUG
int path[MAXVEX][MAXVEX];
typedef struct
{
VertexType vexs[MAXVEX];//顶点表,用来存放顶点数目
EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵,边
int numVertexes, numEdges;
}Graph;
//在图中寻找某个顶点
int Find_Vertex(Graph *g, char ch)
{
int i = 0;
for (i = 0; i < g->numVertexes; i++)
{
if (g->vexs[i] == ch)
break;
}
return i;
}
void CreateGraph(Graph* G)
{
printf("请输入顶点和边数:\n");
scanf("%d %d", &G->numVertexes, &G->numEdges);
#ifdef DEBUG
printf("%d %d\n", G->numVertexes, G->numEdges);
#endif // DEBUG
printf("请输入顶点值:\n");
for (int i = 0; i < G->numVertexes; i++)
{
scanf("%d", &G->vexs[i]);
}
//有向网络逆向值为无穷,正向值为权值,自己到自己为0
//无向网络不连接为0,有连接为权值
for (int i = 0; i < G->numVertexes; i++)
{
for (int j = 0; j < G->numVertexes; j++)
G->arc[i][j] = INFINITY;
}
printf("输入<vi,vj>的两个顶点标号,和权值.\n按Q表示退出\n");
while (getchar() != 'q')
{
int m, n, w;
scanf("%d %d %d", &m, &n, &w);
G->arc[m][n] = w;
G->arc[n][m] = w;
}
}
void Floyd(Graph *G)
{
int i, j, k;
int F[MAXVEX][MAXVEX];
//先初始化矩阵
for (i = 0; i < G->numVertexes; i++)
for (j = 0; j < G->numVertexes; j++)
{
F[i][j] = G->arc[i][j];
path[i][j] = -1;
}
for (k = 0; k < G->numVertexes; k++)
for (i = 0; i < G->numVertexes; i++)
for (j = 0; j < G->numVertexes; j++)
if ((F[i][k] + F[k][j]) < F[i][j])
{
F[i][j] = F[i][k] + F[k][j];
path[i][j] = k;
}
}
void Print_Graph(Graph *G)
{
for (int i = 0; i < G->numVertexes; i++)
{
for (int j = 0; j < G->numVertexes; j++)
{
printf("%d ", G->arc[i][j]);
}
putchar('\n');
}
}
int main()
{
Graph New_Graph;
Graph *G = &New_Graph;
CreateGraph(G);
Print_Graph(G);
return 0;
}