Find the shortest path between cities and visualize it [C language]

Problem Description

Requirements:
1. The adjacency matrix information of the city network graph is stored in the text file mat.txt
2. Find the shortest path from city D to other cities and its minimum cost
3. Visualize the shortest path

The matrix mat is as follows. 99999 means unreachable

0 12 99999 99999 99999 16 14
12 0 10 99999 99999 7 99999
99999 10 0 3 5 6 99999
99999 99999 3 0 4 99999 99999
99999 99999 5 4 0 2 8
16 7 6 99999 2 0 9
14 99999 99999 99999 8 9 0

code

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

#define MAX         7               // 矩阵最大容量
#define INF         (~(0x1<<31))        // 最大值(即0X7FFFFFFF)
#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
#define LENGTH(a)   (sizeof(a)/sizeof(a[0]))

// 邻接矩阵
typedef struct _graph
{
    
    
    char vexs[MAX];       // 顶点集合
    int vexnum;           // 顶点数
    int edgnum;           // 边数
    int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;

// 边的结构体
typedef struct _EdgeData
{
    
    
    char start; // 边的起点
    char end;   // 边的终点
    int weight; // 边的权重
}EData;

/*
 * 返回ch在matrix矩阵中的位置
 */
static int get_position(Graph G, char ch)
{
    
    
    int i;
    for(i=0; i<G.vexnum; i++)
        if(G.vexs[i]==ch)
            return i;
    return -1;
}

/*
 * 读取一个输入字符
 */


/*
 * 创建图(用已提供的矩阵)
 */
Graph* create_example_graph()
{
    
    
    char vexs[] = {
    
    'A', 'B', 'C', 'D', 'E', 'F', 'G'};
	int matrix[MAX][MAX];//定义矩阵
    int lines=0;//矩阵行数
    FILE *fp;
    int count;
    fp = fopen("mat.txt", "r");//打开文件
 if (fp==NULL ) {
    
    //若打开文件失败则退出
        puts("不能打开文件!");
        
    }
    while(lines < MAX)
    {
    
    
        for(count = 0; count < MAX; count ++)
            if(fscanf(fp, "%d",&matrix[lines][count]) == EOF) break;//读取数据
        if(feof(fp)) break;//判断是否文件结束。
        lines++;//读取一行成功,增加行数。
    }
    fclose(fp);//关闭文件。


    int vlen = LENGTH(vexs);
    int i, j;
    Graph* pG;

    // 输入"顶点数"和"边数"
    if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )
        return NULL;
    memset(pG, 0, sizeof(Graph));

    // 初始化"顶点数"
    pG->vexnum = vlen;
    // 初始化"顶点"
    for (i = 0; i < pG->vexnum; i++)
        pG->vexs[i] = vexs[i];

    // 初始化"边"
    for (i = 0; i < pG->vexnum; i++)
        for (j = 0; j < pG->vexnum; j++)
            pG->matrix[i][j] = matrix[i][j];

    // 统计边的数目
    for (i = 0; i < pG->vexnum; i++)
        for (j = 0; j < pG->vexnum; j++)
            if (i!=j && pG->matrix[i][j]!=99999)
                pG->edgnum++;
    pG->edgnum /= 2;

    return pG;
}

/*
 * 返回顶点v的第一个邻接顶点的索引,失败则返回-1
 */
static int first_vertex(Graph G, int v)
{
    
    
    int i;

    if (v<0 || v>(G.vexnum-1))
        return -1;

    for (i = 0; i < G.vexnum; i++)
        if (G.matrix[v][i]!=0 && G.matrix[v][i]!=INF)
            return i;

    return -1;
}

/*
 * 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1
 */
static int next_vertix(Graph G, int v, int w)
{
    
    
    int i;

    if (v<0 || v>(G.vexnum-1) || w<0 || w>(G.vexnum-1))
        return -1;

    for (i = w + 1; i < G.vexnum; i++)
        if (G.matrix[v][i]!=0 && G.matrix[v][i]!=INF)
            return i;

    return -1;
}

/*
 * 打印矩阵队列图
 */
void print_graph(Graph G)
{
    
    
    int i,j;

    printf("Martix Graph:\n");
    for (i = 0; i < G.vexnum; i++)
    {
    
    
        for (j = 0; j < G.vexnum; j++)
            printf("%10d ", G.matrix[i][j]);
        printf("\n");
    }
}
int mat2dot(Graph G,int start,int path[],int path_len,int loss){
    
    

    char d[20];

    sprintf(d,"%c到%c.dot",G.vexs[start],G.vexs[path[0]]);

    FILE *fpWrite=fopen(d,"w");  
    if(fpWrite==NULL)
		
    {
    
       

        return 0;  
    }  

	fprintf(fpWrite,"#@startdo\ngraph demo {\nrankdir=RL\n");
	fprintf(fpWrite,"label=\"%c to %c shortes  loss is %d\" \n",G.vexs[start],G.vexs[path[0]],loss);
	

	fprintf(fpWrite,"%c [color=red]\n",G.vexs[start]);

	 for (int i = 0; i < G.vexnum; i++)

     {
    
     

        for (int j = 0; j <i; j++)

        {
    
    
			if(G.matrix[i][j]>=9999 || G.matrix[i][j]==0){
    
    
				continue;
			}


			int i_is_inpath=false;
			int j_is_inpath=false;
			int kss=0;
			for(kss=0; kss<=path_len; kss++){
    
    

				if(path[kss]==j){
    
    
					j_is_inpath=true;
					fprintf(fpWrite,"%c [color=red]\n",G.vexs[j]);
				}
				else if(path[kss]==i){
    
    
					fprintf(fpWrite,"%c [color=red]\n",G.vexs[i]);
						i_is_inpath=true;	
				}else
				{
    
    }
					
			}
			if(i_is_inpath && j_is_inpath){
    
    
			   fprintf(fpWrite," %c -- %c [label=%d,color=red, fontcolor=red]\n",G.vexs[i],G.vexs[j],G.matrix[i][j]);  
			}
			else{
    
    
			fprintf(fpWrite," %c -- %c [label=%d]\n",G.vexs[i],G.vexs[j],G.matrix[i][j]);  
			}
		
		}
	 }
	 fprintf(fpWrite,"}\n#@enddot");
	 fclose(fpWrite); 
	 char cmd[50];
	 

	 sprintf(cmd,"dot %s -T png -o %c到%c.png",d,G.vexs[start],G.vexs[path[0]]);
	 
	 system(cmd);
	 return 0;  
}



/*
 * Dijkstra最短路径。
 * 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。
 *
 * 参数说明:
 *        G -- 图
 *       vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。
 *     prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。
 *     dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。
 */
void dijkstra(Graph G, int vs, int prev[], int dist[])
{
    
    
    int i,j,k;
    int min;
    int tmp;
    int flag[MAX];      // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。

    // 初始化
    for (i = 0; i < G.vexnum; i++)
    {
    
    
        flag[i] = 0;              // 顶点i的最短路径还没获取到。
        prev[i] = 0;              // 顶点i的前驱顶点为0。
        dist[i] = G.matrix[vs][i];// 顶点i的最短路径为"顶点vs"到"顶点i"的权。
    }

    // 对"顶点vs"自身进行初始化
    flag[vs] = 1;
    dist[vs] = 0;

    // 遍历G.vexnum-1次;每次找出一个顶点的最短路径。
    for (i = 1; i < G.vexnum; i++)
    {
    
    
        // 寻找当前最小的路径;
        // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
        min = INF;
        for (j = 0; j < G.vexnum; j++)
        {
    
    
            if (flag[j]==0 && dist[j]<min)
            {
    
    
                min = dist[j];
                k = j;
            }
        }
        // 标记"顶点k"为已经获取到最短路径
        flag[k] = 1;

        // 修正当前最短路径和前驱顶点
        // 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
        for (j = 0; j < G.vexnum; j++)
        {
    
    
            tmp = (G.matrix[k][j]==INF ? INF : (min + G.matrix[k][j])); // 防止溢出
            if (flag[j] == 0 && (tmp  < dist[j]) )
            {
    
    
                dist[j] = tmp;
                prev[j] = k;
            }
        }
    }



    // 打印dijkstra最短路径的结果



    printf("从城市%c 出发 \n", G.vexs[vs]);
    for (i = 0; i < G.vexnum; i++){
    
    
		if(i==vs)
			continue;
		printf("%c到%c的最小代价为: %d\n", G.vexs[vs], G.vexs[i], dist[i]);

		// 建立一个栈转置用 
		char stack[MAX];
		int path[MAX];

		int top = 0 ;
		stack[top++]=G.vexs[i];
		path[top-1]=i;
		int k =i ;
		while(prev[k]!=0){
    
    
			stack[top++]=G.vexs[prev[k]];
			path[top-1]=prev[k];
			k=prev[k];
		}
		stack[top]=G.vexs[vs];
		path[top]=vs;
		printf("路径为:");
		mat2dot(G,vs, path,top,dist[i]);


		while(top!=-1){
    
    
			if(top ==0)
				printf("%c",stack[top--]);
			else
			printf("%c->",stack[top--]);
		}
		printf("\n");
	}


}

void main()
{
    
    
    int prev[MAX] = {
    
    0};
    int dist[MAX] = {
    
    0};
    Graph* pG;

    // 自定义"图"(输入矩阵队列)
    //pG = create_graph();
    // 采用已有的"图"
    pG = create_example_graph();
	//print_graph(*pG);


    dijkstra(*pG, 3, prev, dist);
/**
 *
 * 部分引用 @author skywang
 * @date 2014/04/24
 */
}

running result

Insert image description here

The software graphviz needs to be installed for visualization.

Some main problems encountered

1. How to read a text file?
Use the fopen function to read a text file, and then use the fscanf function to store the read file into the matrix matrix. This completes the reading of the text file. See the comments for details.

	int matrix[MAX][MAX];//定义矩阵
    int lines=0;//矩阵行数
    FILE *fp;
    int count;
    fp = fopen("mat.txt", "r");//打开文件
 if (fp==NULL ) {
    
    //若打开文件失败则退出
        puts("不能打开文件!");
        
    }
    while(lines < MAX)
    {
    
    
        for(count = 0; count < MAX; count ++)
            if(fscanf(fp, "%d",&matrix[lines][count]) == EOF) break;//读取数据
        if(feof(fp)) break;//判断是否文件结束。
        lines++;//读取一行成功,增加行数。
    }
    fclose(fp);//关闭文件。

2. How to visualize graph structure?
A software called graphviz is used here, which can also be used as a plug-in. This is programmed using dot language. Convert the original graph structure into the corresponding dot code, and then execute this code to generate the corresponding visual interface. For example,
copy the following code and test it

#@startdot
graph demo {
    
    
    a--b
    a--c
}
#@enddot

Click here to test and
it will generate such a result. For more detailed dot language teaching, see here

3. How to concatenate strings and write them into a file?
The most familiar one is the printf function that performs console output and writes the output to a file. This requires the file-related function fprint to input strings into the file stream. First the file pointer is opened, then the writing operation occurs, then the file pointer is closed.

    FILE *fpWrite=fopen("a.txt","w");  
    if(fpWrite==NULL)
        return 0;  
	fprintf(fpWrite,"hello world");
	fclose(fpWrite)

4. How to dynamically generate file names and input different shortest paths into different dot files?
Because you want to write different shortest paths into different files, the file name should constantly change according to the content, not just "data.txt". Then you need to define a character array to store the file name. Then use sprintf to perform writing operations. The code is as follows

    char filename[20];
    sprintf(filename,"%c到%c.dot",G.vexs[start],G.vexs[path[0]]);
    FILE *fpWrite=fopen(filename,"w");  

Experience

Through this experiment, I learned more about the practical application of Dijkstra's shortest path algorithm, so that the data structure knowledge I learned did not just stay in books. Among them, what impressed me deeply was the visualization of graph structure. Because I had never been exposed to related knowledge before, I was stuck for a long time. Finally, I saw a blogger using graphviz to visualize his binary tree structure, and then I thought if it could be done. Migrating to the representation of graph structure, after consulting the official documents, I finally found a solution for graph structure visualization, and successfully completed this experiment. In this experiment, I not only completed the basic content of the experiment, but also realized the improvement of my comprehensive ability and information integration ability, such as consulting relevant information codes, integrating these different methods, and serving my own experiment. At the same time, this time The completion of the experiment also improved my confidence in programming, and I benefited a lot from it.

Guess you like

Origin blog.csdn.net/qq_41563601/article/details/123662444