最短路徑Dijkstra和Floy算法

最短路徑問題一般分爲兩種情況,單源最短路徑(即從一個點出發到其餘各點的最短路徑問題)和每對頂點之間的最短路徑問題。Dijkstra和Floy算法相比之下我更喜歡Floy算法,該算法容易理解,思路簡潔。

兩種算法解決最短路徑都是基於貪心的算法,從局部出發一點點擴展。

以一個簡單的例子來說,如下圖,圖中有6點,可以看做是6個城市,求各城市之間的最短路徑問題。這裏圖中只有6個頂點,你可能僅憑眼看就能看出來最短路徑,就是一個暴力枚舉解決的過程,但是當結點數增多,枚舉效率是很慢很耗時的,還有如何將眼睛看得到最短路徑的結果這個過程抽象成代碼,然後編程解決,是很考察一個人的抽象能力、問題建模。

Dijkstra算法把這個圖裏面的所有頂點分成兩個集合S和V-S,集合S中存放已經找到最短路徑的頂點,V-S中存放當前還未找到最短路徑的頂點。這裏就體現貪心的思想了,當我去解決一個還未找到最短路徑的頂點時,我可以在已經找到最短路徑的集合裏面找這些頂點是否有達到未找到最短路徑的頂點的路徑,這樣子的走法是否比我直接走那個點近。算法按照最短路徑長度遞增的順序逐個將V-S集合中的元素加入到S集合中來,直到所有的元素都加入到集合中,就完成了問題的求解。

下一個待解決頂點的最短路徑只有兩種可能:

1、直接從出發點到該點。

2、從出發點經過以求得最短路徑的某個點,再到達待解決頂點,由兩段路線組成。

這是這個算法的主要思想。大概的流程如下

核心算法:

void Dijkstra(const GRAPH *graph, int *dist) {
	int min;
	int k;
	int j;
	int t;
	int i;
	int *flag;

	flag = (int *)calloc(sizeof(int), graph->vexnum);
	for(j = 1, flag[0] = 1; j < graph->vexnum; j++) {
		dist[j] = graph->arc[0][j];
	}
	for(t = 1; t < graph->vexnum; t++) {
		min = INFINITY;
		k = 0;
		// 找到當前記錄最短路徑的數組dist 中最小的路徑 還有達到的結點
		for(i = 1; i < graph->vexnum; i++) {
			if((flag[i] == 0) && (dist[i] < min)) {
				min = dist[i];
				k = i;
			}
		}
		flag[k] = 1;
		// 以這個找到的k結點 以這個結點爲中間結點出發去判斷 通過該節點達到其他結點是夠更近
		for(i = 1; i < graph->vexnum; i++) {
// 在這裏如果發現了 從已經找到最短路徑的節點間接達到待解決點 要更近如果有這樣的路徑
// 就更新記錄最短路徑的數組
			if((flag[i] == 0) && (dist[k] + graph->arc[k][i] <= dist[i])) {
				dist[i] = dist[k] + graph->arc[k][i];
			}
		}
	}
}

Floy算法比起上面的算法理解起來要簡單的多,該算法的時間複雜度是O(N^3),可以解決各頂點之間的最短路徑。而Dijkstra解決的是一個頂點到其他頂點的問題,時間複雜度是O(N^2),若要用Dijkstra算法解決各頂點之間的最短路徑問題,外層再加上一層循環,總的時間複雜度也是O(N^3)。所以兩個算法時間複雜度都差不多。

FLoy算法引入一個和圖一樣大小的矩陣,叫做路由矩陣,來表示達到目標節點的前一個經過的結點。

這個算法的整體思想就是,比如現在有結點A,B,C三個結點,當以A爲中間結點的時候,判斷B->C還是B->A->C更近。然後繼續以B,C爲中間結點,做同樣的操作。

算法過程如下。

核心代碼:

void Floy(const GRAPH *graph, int **Map_info, int **rount_matrix) {
	int i;
	int j;
	int k;

	for(i = 0; i < graph->vexnum; i++) {
		for(j = 0; j < graph->vexnum; j++) {
			Map_info[i][j] = graph->arc[i][j];
			rount_matrix[i][j] = j;
		}
	}

	for(i = 0; i <  graph->vexnum; i++) {
		for(j = 0; j <  graph->vexnum; j++) {
			for(k = 0; k <  graph->vexnum; k++) {
				if(Map_info[j][k] > Map_info[j][i] + Map_info[i][k]) {
					Map_info[j][k] = Map_info[j][i] + Map_info[i][k];
					rount_matrix[j][k] = i;
				}
			}
		}
	}
}

 

附上兩個算法的完整代碼和結果

#include<stdio.h>
#include<malloc.h>

#include"../../include/kwenarraytools.h"

#define MAXVEXNUM	100

#define INFINITY	99999

typedef struct GRAPH{
	int vexnum;						//頂點個數
	int edgenum;					//邊的個數
	int arc[MAXVEXNUM][MAXVEXNUM];	//圖的鄰接矩陣
	char *vec_infor;					//記錄各個頂點的信息 長度和vexnum保持一致
}GRAPH;

void createGraph(GRAPH **graph, int vexnum, int edgenum);
void destoryGraph(GRAPH **graph);


void Dijkstra(const GRAPH *graph, int *dist);
void showMatrix_1(int array[][MAXVEXNUM], int n);

//map_info記錄的各點到各點的距離信息, rout_matrix是路由矩陣 達到目標點最後一次經過的結點
void Floy(const GRAPH *graph, int **Map_info, int **rount_matrix);

void showArray(int *ArrayFirstAddress, int array_length);


void Floy(const GRAPH *graph, int **Map_info, int **rount_matrix) {
	int i;
	int j;
	int k;

	for(i = 0; i < graph->vexnum; i++) {
		for(j = 0; j < graph->vexnum; j++) {
			Map_info[i][j] = graph->arc[i][j];
			rount_matrix[i][j] = j;
		}
	}

	for(i = 0; i <  graph->vexnum; i++) {
		for(j = 0; j <  graph->vexnum; j++) {
			for(k = 0; k <  graph->vexnum; k++) {
				if(Map_info[j][k] > Map_info[j][i] + Map_info[i][k]) {
					Map_info[j][k] = Map_info[j][i] + Map_info[i][k];
					rount_matrix[j][k] = i;
				}
			}
		}
	}
}

void Dijkstra(const GRAPH *graph, int *dist) {
	int min;
	int k;
	int j;
	int t;
	int i;
	int *flag;

	flag = (int *)calloc(sizeof(int), graph->vexnum);
	for(j = 1, flag[0] = 1; j < graph->vexnum; j++) {
		dist[j] = graph->arc[0][j];
	}
	for(t = 1; t < graph->vexnum; t++) {
		min = INFINITY;
		k = 0;
		// 找到當前記錄最短路徑的數組dist 中最小的路徑 還有達到的結點
		for(i = 1; i < graph->vexnum; i++) {
			if((flag[i] == 0) && (dist[i] < min)) {
				min = dist[i];
				k = i;
			}
		}
		flag[k] = 1;
		// 以這個找到的k結點 以這個結點爲中間結點出發去判斷 通過該節點達到其他結點是夠更近
		for(i = 1; i < graph->vexnum; i++) {
// 在這裏如果發現了 從已經找到最短路徑的節點間接達到待解決點 要更近如果有這樣的路徑
// 就更新記錄最短路徑的數組
			if((flag[i] == 0) && (dist[k] + graph->arc[k][i] <= dist[i])) {
				dist[i] = dist[k] + graph->arc[k][i];
			}
		}
	}
}



void destoryGraph(GRAPH **graph) {
	if((*graph) == NULL) {
		return;
	}
	free((*graph)->vec_infor);
	free(*graph);
	*graph = NULL;
}

void createGraph(GRAPH **graph, int vexnum, int edgenum) {
	int i;
	int data;
	int from;
	int to;
	int j;

	*graph = (GRAPH *)calloc(sizeof(GRAPH), 1);
	(*graph)->vexnum = vexnum;
	(*graph)->edgenum = edgenum;
	(*graph)->vec_infor = (char *)calloc(sizeof(char), 1);
	for(i = 0; i < vexnum; i++) {
		printf("input the %d/%d vex infor: ", i+1, vexnum);
		setbuf(stdin, NULL);
		(*graph)->vec_infor[i] = getchar();
	}
	for(i = 0; i < vexnum; i++) {
		for(j = 0; j < vexnum; j++) {
			(*graph)->arc[i][j] = INFINITY;
		}
	}

	for(i = 0; i < vexnum; i++) {
		(*graph)->arc[i][i] = 0;
	}

	for(i = 0; i < edgenum; i++) {
		printf("input the %d/%d ede infor(from to power):", i+1, edgenum);
		setbuf(stdin, NULL);
		scanf("%d%d%d", &from, &to, &data);
		// printf("from = %d, to = %d, data = %d\n", from, to, data);
		(*graph)->arc[from][to] = (*graph)->arc[to][from]= data;
	}
}

int main(void) {
	GRAPH *graph = NULL;
	int *dij_dist;
	int vexnum = 4;       		//頂點個數
	int edgenum = 5;			//邊數
	int **Floy_result_matrix;
	int **Floy_rount_matrix;

	printf("input vexnum and edgenum: ");
	scanf("%d%d", &vexnum, &edgenum);

	dij_dist = (int *)calloc(sizeof(int), vexnum);
	createGraph(&graph, vexnum, edgenum);
	printf("Map Matrix infor:\n");
	showMatrix_1(graph->arc, vexnum);
	
	Dijkstra(graph, dij_dist);

	printf("Dijkstra result: \n");
	showArray(dij_dist, vexnum);  //這裏打印的是第一個節點到其他各點的最短距離信息
	
	Floy_result_matrix = getMatrix(vexnum, vexnum);
	Floy_rount_matrix = getMatrix(vexnum, vexnum);
	Floy(graph, Floy_result_matrix, Floy_rount_matrix);

	printf("Floy result: \n");
	showMatrix(&Floy_result_matrix[0][0], vexnum, vexnum);
	showMatrix(&Floy_rount_matrix[0][0], vexnum, vexnum);


	destoryMatrix(&Floy_result_matrix, vexnum);
	destoryMatrix(&Floy_rount_matrix, vexnum);
	destoryGraph(&graph);
	free(dij_dist);

	return 0;
}

void showMatrix_1(int array[][MAXVEXNUM], int n) {
	int i;
	int j;

	for(i = 0; i < n; i++) {
		for(j = 0; j < n; j++) {
			// printf("%d ", array[i][j]);
			printf("%6d ", *(*(array + i)+j));
		}
		printf("\n");
	}
	printf("\n");
}
/* 
6 10
1
2
3
4
5
6
0 1 10
0 2 21
0 4 8
1 2 18
1 3 5
1 5 6
2 4 25
2 5 19
3 5 7
4 5 33

*/

自己寫的矩陣輸出 銷燬函數 頭文件中有用到

void showArray(int *ArrayFirstAddress, int array_length);

void showMatrix(int *MatrixFisrtAddress, int row, int col) {
	int i;
	int j;

	for(i = 0; i < row; i++) {
		for(j = 0; j < col; j++) {
			printf("%5d", *(MatrixFisrtAddress++));
		}
		printf("\n");
	}
	printf("\n\n");
}
//銷燬數組
void destoryArray(int **array);

void destoryMatrix(int ***array, int row) {
	int i;

	for(i = 0; i < row; i++) {
		free((*array)[i]);
		(*array)[i] = NULL;
	}
}

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章