基于邻接表的dijkstra算法——C语言实现

鉴于CSDN上实现dijkstra算法大多是使用邻接矩阵,因此记录一下使用邻接表的实现。
该算法通过读入文件构造邻接表,然后使用dijkstra构造单源最短路径。
代码如下:
dijkstra.h

#define MAXSIZE 210
//定义无向图中的顶点

//定义无向图中的边(弧)
typedef struct Edge{
	int node;//该边的终点
	int cost;//经过这条边需要的代价
	struct Edge* nextedge;//与该顶点相连的下一条边
}Edge;

typedef struct Node{
	Edge* firstedge;//该顶点所对应的边表的头节点
}Node;

//定义邻接表
typedef struct {
	Node* list[MAXSIZE];
	int nodecount;
	int edgecount;
}AdjacentList;

dijkstra.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "dijkstra.h"

#pragma warning(disable:4996)

#define INF  1000000
#define BUFFERSIZE 255

//初始化邻接表
AdjacentList* initList() {
	AdjacentList* list = (AdjacentList*)malloc(sizeof(AdjacentList));
	if (list == NULL) {
		printf("can not create a list\n");
		return NULL;
	}
	memset(list->list, 0, sizeof(list->list));
	list->nodecount = 0;
	list->edgecount = 0;
	return list;
}

//创建新的顶点
Node* createNode() {
	Node* node = (Node*)malloc(sizeof(Node));
	if (node == NULL) {
		printf("can not create a node\n");
		return NULL;
	}
	node->firstedge = NULL;
	return node;
}

//创建新的边
Edge* createEdge(int node, int cost) {
	Edge* edge = (Edge*)malloc(sizeof(Edge));
	if (edge == NULL) {
		printf("can not create an edge\n");
		return NULL;
	}
	edge->cost = cost;
	edge->node = node;
	edge->nextedge = NULL;
	return edge;
}


//采用头插法向边表中插入新的边
int insertEdge(Node* firstnode, Edge* newedge) {
	if (firstnode == NULL) {
		return 0;
	}
	if (firstnode->firstedge == NULL) {
		firstnode->firstedge = newedge;
		return 1;
	}
	newedge->nextedge = firstnode->firstedge;
	firstnode->firstedge = newedge;
	return 1;
}

//构造邻接表
AdjacentList* createList(AdjacentList* list, char* filename) {
	FILE* infile;
	Edge* edge;
	char* split = " \t";
	int node = -1;
	char buffer[BUFFERSIZE];
	if ((infile = fopen(filename, "rt")) == NULL) {
		fputs(" Couldn't open the file.\n", stdout);
		return NULL;
	}
	char* p;//保存每一行最开始的顶点号
	//根据逗号分割字符串后,用于保存边表元素的右半部分
	char* right;
	int edgenode;
	int edgecost;
	while (!feof(infile)) {
		if (fgets(buffer, BUFFERSIZE, infile) == NULL) {
			break;
		}
		buffer[strlen(buffer)] = '\0';
		p = strtok(buffer, split);
		node = atoi(p);
		if (list->list[node] == NULL) {
			//如果邻接表中尚无该顶点,构造
			list->list[node] = createNode();
		}
		list->nodecount++;
		p = strtok(NULL, split);
		//字符串剩余部分为边表信息,循环操作
		for (; p!=NULL && *p!='\n';) {
			int i = 0;//用于指示","出现的位置
			while (p[i] != ',') { i++; }
			right = strdup(p + i + 1);
			edgecost = atoi(right);
			p[i] = '\0';
			edgenode = atoi(p);
			//构造新的边,并顶点对应的边表
			edge = createEdge(edgenode, edgecost);
			int res = insertEdge(list->list[node], edge);
			if (res == 0) {
				printf("can not insert edge %d,%d \n", edgenode, edgecost);
				exit(EXIT_FAILURE);
			}
			list->edgecount++;
			p = strtok(NULL, split);
		}

	}
	return list;
}


//计算从源顶点到其他顶点的单源最短路径
void shortestpath(AdjacentList* list, int visited[], int distance[], int prenode[], int src) {
	int count = 0;//记录直接与源点相连的顶点个数
	visited[src] = 1;//源点已经添加到最短路径中
	distance[src] = 0;
	count++;
	//如果有顶点和源点直接相连,另外设置距离
	if (list->list[src] != NULL) {
		Edge* p = list->list[src]->firstedge;
		for (; p != NULL; ) {
			distance[p->node] = p->cost;
			prenode[p->node] = src;//这些顶点的前驱顶点为源点
			p = p->nextedge;
		}
	}
	
	while (count <= list->nodecount) {
		int min = INF;
		int target_node = 0;
		//首先在剩余节点中寻找距离最新加入最短路径的顶点,最近的那一个
		for (int i = 1; i <= list->nodecount; i++) {
			if (visited[i] == 0 && min > distance[i]) {
				min = distance[i];
				target_node = i;
			}
		}
		visited[target_node] = 1;//将最短距离顶点加入最短路径中
		count++;
		//已新加入的顶点为中介,更新剩余未加入最短路径的顶点的距离
		if (list->list[target_node] != NULL) {
			Edge* p = list->list[target_node]->firstedge;
			for (; p != NULL; ) {
				if (visited[p->node] == 0 && distance[target_node] + p->cost < distance[p->node]) {
					distance[p->node] = distance[target_node] + p->cost;
					prenode[p->node] = target_node;
				}
				p = p->nextedge;
			}
		}


	}


}

int main(int argc, char* argv[]) {
	//判断
	if (argc != 2)
	{
		fprintf(stderr, "please enter the file \n");
		exit(EXIT_FAILURE);
	}
	AdjacentList* list;
	list = initList();
	list = createList(list, "data.txt");
	int visited[201] = {0};//标记顶点是否已经被记录为最短路径
	int distance[201];//标记从源点到其他顶点的路径长度
	int prenode[201] = {0};//如果某个顶点要加入最短路径,记录该顶点在最短路径中的前一个顶点
	//初始将每个顶点的距离设置为无穷大
	for (int i = 0; i < 201; i++) {
		distance[i] = INF;
	}

	int src = 0;
	char input[4];
	printf("请输入源点序号(1——200):  ");
	scanf("%s", input);
	input[strlen(input)] = '\0';
	src = atoi(input);
	if (src <= 0 || src > 200) {
		printf("\n序号不合法,请输入正确序号\n");
		return 0;
	}
	shortestpath(list, visited, distance, prenode, src);
	int path[200] = { 0 };//用于保存路径信息

	//输出最短路径信息
	for (int i = 1; i <= 200; i++) {
		if (distance[i] == INF) {
			printf("顶点 %d 到顶点 %d 之间没有最短路径\n", src, i);
		}
		else {
			int p = i;
			int n = 0;
			while (p != 0) {
				path[n] = p;
				p = prenode[p];//使p指向自己的前驱节点
				n++;
			}
			//n为路径中节点数量
			printf("顶点 %d 到顶点 %d 之间的最短路径为: ", src, i);
			printf("%d", path[n - 1]);
			for (int j = n - 2; j >= 0; j--) {
				printf("—>%d", path[j]);
			}
			printf("   路径长度为 %d\n", distance[i]);
		}
		memset(path, 0, 200);
	}

	return 0;
}

以下为测试用例:‘data.txt’
链接: https://pan.baidu.com/s/1MKQO6IzYfMkutv3ozHOtGw 提取码: 81tu

时间复杂度分析:该算法是中规中矩的实现,时间复杂度为O(n^2),如果有同学有兴趣,可以在“查找最短距离的顶点”这一块,使用堆排序来优化,这样的话,时间复杂度应该为O(nlogn), n为无向图的顶点个数。

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是使用邻接表实现Dijkstra算法求单源最短路径的C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX_VERTICES 1000 typedef struct Edge { int dest; int weight; struct Edge* next; } Edge; typedef struct Node { Edge* head; } Node; typedef struct Graph { int num_vertices; Node nodes[MAX_VERTICES]; } Graph; Graph* create_graph(int num_vertices) { Graph* graph = (Graph*) malloc(sizeof(Graph)); graph->num_vertices = num_vertices; for (int i = 0; i < num_vertices; i++) graph->nodes[i].head = NULL; return graph; } Edge* create_edge(int dest, int weight) { Edge* edge = (Edge*) malloc(sizeof(Edge)); edge->dest = dest; edge->weight = weight; edge->next = NULL; return edge; } void add_edge(Graph* graph, int src, int dest, int weight) { Edge* edge = create_edge(dest, weight); edge->next = graph->nodes[src].head; graph->nodes[src].head = edge; } int min_distance(int dist[], int visited[], int num_vertices) { int min_dist = INT_MAX, min_vertex; for (int i = 0; i < num_vertices; i++) { if (!visited[i] && dist[i] < min_dist) { min_dist = dist[i]; min_vertex = i; } } return min_vertex; } void print_solution(int dist[], int num_vertices) { printf("Vertex\tDistance from Source\n"); for (int i = 0; i < num_vertices; i++) printf("%d\t%d\n", i, dist[i]); } void dijkstra(Graph* graph, int src) { int dist[MAX_VERTICES]; int visited[MAX_VERTICES]; for (int i = 0; i < graph->num_vertices; i++) { dist[i] = INT_MAX; visited[i] = 0; } dist[src] = 0; for (int i = 0; i < graph->num_vertices - 1; i++) { int u = min_distance(dist, visited, graph->num_vertices); visited[u] = 1; for (Edge* edge = graph->nodes[u].head; edge != NULL; edge = edge->next) { int v = edge->dest; int weight = edge->weight; if (!visited[v] && dist[u] != INT_MAX && dist[u] + weight < dist[v]) dist[v] = dist[u] + weight; } } print_solution(dist, graph->num_vertices); } int main() { int num_vertices = 5; Graph* graph = create_graph(num_vertices); add_edge(graph, 0, 1, 10); add_edge(graph, 0, 4, 5); add_edge(graph, 1, 2, 1); add_edge(graph, 1, 4, 2); add_edge(graph, 2, 3, 4); add_edge(graph, 3, 2, 6); add_edge(graph, 3, 0, 7); add_edge(graph, 4, 1, 3); add_edge(graph, 4, 2, 9); dijkstra(graph, 0); return 0; } ``` 这里实现Dijkstra算法使用了邻接表来表示图,使用了堆排序的思想来寻找当前距离源点最近的未访问过的节点,以提高算法效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值