数据结构与C语言实现(十)——图(下):Dijkstra与Floyd

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值