数据结构之有关图的算法(图的邻接表示法)
同前篇一样,本篇意在总结有关图的一些基本算法
包括有图的最小生成树(Prim,Kruscal),最短路径(Dijkstra,Floyd),拓扑排序等算法
本篇图的数据结构为邻接表表示法
首先graph.h
#ifndef __GRAPH_H__ #define __GRAPH_H__ typedef struct graph *Graph; Graph graph_create(int n); void graph_destroy(Graph); void graph_add_edge(Graph g, char u, char v,unsigned int wt); int graph_vertex_count(Graph); int graph_edge_count(Graph); int graph_out_degree(Graph, char svex); int graph_has_edge(Graph, char svex, char tvex); void graph_foreach(Graph g, char svex, void (*f)(Graph g, char svex, char tvex, void *data), void *data); #endif
cpp文件
// 图.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdlib.h> #include<stdio.h> #include <string.h> #include <assert.h> #include "graph.h" /* 代码摘自一位yale前辈 */ struct graph { int vexnum; /* number of vertices */ int edgenum; /* number of edges */ struct successors { char vexname; char is_sorted; /* true if list is already sorted */ int size; /* number of successors,即出度数*/ int capacity; /* number of slots in array,出度数组的长度,当空间不够,它就会两倍增加*/ struct outvexs{ char vex; unsigned int weight; } list[1];//出度数组。保存信息有顶点和权值 } *alist[1];//alist数组相当于顶点链表,n个顶点就有n个元素,这里同样是为了动态增加 }; /* create a new graph with n vertices labeled 0..n-1 and no edges */ Graph graph_create(int n) { Graph g; int i; //新增一个graph空间和n-1个successors指针,算上graph中的一个successors指针就有n个了 g = (Graph)malloc(sizeof(struct graph) + sizeof(struct successors *) * (n-1)); //程序中大量使用assert,其作用是如果它的条件返回错误,则终止程序执行 assert(g); g->vexnum = n; g->edgenum = 0; for(i = 0; i < n; i++) { //顶点链表 g->alist[i] = (graph::successors*)malloc(sizeof(graph::successors)); assert(g->alist[i]); g->alist[i]->vexname = 'A'+i; g->alist[i]->size = 0; g->alist[i]->capacity = 1; g->alist[i]->is_sorted= 1; } return g; } /* free all space used by graph */ void graph_destroy(Graph g) { int i; for(i = 0; i < g->vexnum; i++){ free(g->alist[i]); } free(g); } //找到vex在g->alist数组中的位置 int findVex(Graph g,char vex){ for(int i=0;i<g->vexnum;i++){ if(g->alist[i]->vexname==vex) return i; } return -1; } /* 为graph添加边,这里是单向边,仅<u,v> */ void graph_add_edge(Graph g, char u, char v,unsigned int wt) { int s=findVex(g,u); int t=findVex(g,v); assert(s >= 0); assert(s < g->vexnum); assert(t >= 0); assert(t < g->vexnum); /* do we need to grow the list? */ while(g->alist[s]->size >= g->alist[s]->capacity) { g->alist[s]->capacity *= 2;//容量两倍增加的方式 g->alist[s] =(graph::successors*)realloc(g->alist[s], sizeof(graph::successors) + sizeof(graph::successors::outvexs) * (g->alist[s]->capacity - 1)); } /* now add the new sink */ g->alist[s]->list[g->alist[s]->size].vex = v; g->alist[s]->list[g->alist[s]->size++].weight = wt; g->alist[s]->is_sorted = 0; /* bump edge count */ g->edgenum++; } /* return the number of vertices in the graph */ int graph_vertex_count(Graph g) { return g->vexnum; } /* return the number of vertices in the graph */ int graph_edge_count(Graph g) { return g->edgenum; } /* return the out-degree of a vertex */ int graph_out_degree(Graph g, char vex) { int source = findVex(g,vex); assert(source >= 0); assert(source < g->vexnum); return g->alist[source]->size; } /* when we are willing to call bsearch,二分查找 */ #define BSEARCH_THRESHOLD (10) static int intcmp(const void *a, const void *b) { return (*(const graph::successors::outvexs *)a).vex - (*(const graph::successors::outvexs*)b).vex; } /* return 1 if edge (source, sink) exists), 0 otherwise */ int graph_has_edge(Graph g, char svex, char tvex) { int source=findVex(g,svex);; int sink=findVex(g,tvex); assert(source >= 0); assert(source < g->vexnum); assert(sink >= 0); assert(sink < g->vexnum); //如果该顶点出度数超过10,才使用二分查找 if(graph_out_degree(g, svex) >= BSEARCH_THRESHOLD) { if(! g->alist[source]->is_sorted) { qsort(g->alist[source]->list, g->alist[source]->size, sizeof(graph::successors::outvexs), intcmp); } /* call bsearch to do binary search for us */ graph::successors::outvexs *list=(graph::successors::outvexs *) bsearch(&tvex, g->alist[source]->list, g->alist[source]->size, sizeof(graph::successors::outvexs), intcmp); if(list) return 1; else return 0; } else { /* just do a simple linear search */ /* we could call lfind for this, but why bother? */ for(int i = 0; i < g->alist[source]->size; i++) { if(g->alist[source]->list[i].vex == tvex) return 1; } /* else */ return 0; } } /* invoke f on all edges (u,v) with source u */ /* supplying data as final parameter to f */ //这里注意回调函数的使用 void graph_foreach(Graph g, char svex, void (*f)(Graph g, char svex, char tvex, void *data), void *data) { int i; int source=findVex(g,svex); assert(source >= 0); assert(source < g->vexnum); for(i = 0; i < g->alist[source]->size; i++) { f(g, svex, g->alist[source]->list[i].vex, data); } } #define TEST_SIZE (6) //static使得本函数本文件可见 static void match_sink(Graph g, char svex, char tvex, void *data) { assert(data && tvex == (*(graph::successors::outvexs *) data).vex); } //这个函数有什么用? static int node2dot(Graph g) { assert(g != NULL); return 0; } static void print_edge2dot(Graph g,char source, char sink, void *data) { int svex_pos=findVex(g,source); int tvex_pos; for(int i=0;i<g->alist[svex_pos]->size;i++) if(sink == g->alist[svex_pos]->list[i].vex){ tvex_pos=i; break; } fprintf(stdout,"<%c->%c>:weight:%d\n",source,sink,g->alist[svex_pos]->list[tvex_pos].weight); } //打印所有的边 static int edge2dot(Graph g) { assert(g != NULL); int idx = 0; int node_cnt = graph_vertex_count(g); for(idx = 0;idx<node_cnt; idx++) { graph_foreach(g,g->alist[idx]->vexname,print_edge2dot,NULL); } printf("\n"); return 0; } int graph2dot(Graph g) { fprintf(stdout,"digraph{"); node2dot(g); edge2dot(g); fprintf(stdout,"}n"); return 0; } //最小生成树之Prim,Kruscal算法 //松弛操作 struct EdgeType{ char s,t; unsigned int cost; }; void relaxation(Graph g,EdgeType *dist,char vex,bool* visited){ int svex_pos=findVex(g,vex); assert(svex_pos!=-1); for(int j=0;j<g->alist[svex_pos]->size;j++){ int tvex_pos = findVex(g,g->alist[svex_pos]->list[j].vex); assert(tvex_pos!=-1); if(visited[tvex_pos]==false && g->alist[svex_pos]->list[j].weight<dist[tvex_pos].cost){ dist[tvex_pos].cost=g->alist[svex_pos]->list[j].weight; dist[tvex_pos].s=vex; dist[tvex_pos].t=g->alist[svex_pos]->list[j].vex; } } } const unsigned int INFINITY = -1; int findMin(EdgeType *dist,bool* visited,int num){ unsigned int min=INFINITY; int pos=-1; for(int i=0;i<num;i++) if(visited[i]==false && min>dist[i].cost){ min=dist[i].cost; pos=i; } return pos; } //MST之Prim算法 void MST_Prim(Graph g,char beg_vex){ EdgeType *dist = (EdgeType*)malloc(sizeof(EdgeType)*g->vexnum);//存放MST的边,MST边 = vexnum-1 // memset(dist,0xFF,sizeof(int)*g->vexnum); bool *visited=(bool*)malloc(sizeof(bool)*g->vexnum);//是否已添加 memset(visited,0,sizeof(bool)*g->vexnum); // char *prim_vex=(char*)malloc(g->vexnum+1);//prim_vex数组保存添加的结点 int vexs=0;//记录已加入点的数目 // prim_vex[vexs++]=beg_vex; int i=findVex(g,beg_vex); assert(i!=-1); visited[i]=true; vexs++; relaxation(g,dist,beg_vex,visited); while(vexs<g->vexnum){ int i=findMin(dist,visited,g->vexnum); assert(i!=-1); visited[i]=true; vexs++; printf("<%c,%c>:%d\n",dist[i].s,dist[i].t,dist[i].cost); relaxation(g,dist,g->alist[i]->vexname,visited); } free(dist); free(visited); } int cmp(const void* a,const void *b){ return (*(EdgeType *)a).cost - (*(EdgeType *)b).cost; } //寻找vex所在树的根结点 int findRoot(Graph g,int *root,char vex){ int t=findVex(g,vex); while(root[t]>=0) t=root[t]; return t; } void MST_Kruscal(Graph g){ int k=0; EdgeType *edges = new EdgeType[g->edgenum]; for(int i=0;i<g->vexnum;i++){ for(int j=0;j<g->alist[i]->size;j++){//有向,因此不考虑<u,v>和<v,u>的问题 edges[k].s=g->alist[i]->vexname; edges[k].t=g->alist[i]->list[j].vex; edges[k++].cost=g->alist[i]->list[j].weight; } } //所有边按从小到大排序 qsort(edges,g->edgenum,sizeof(EdgeType),cmp); //root[i]表示顶点i所在的树的根结点 int *root=(int*)malloc(sizeof(int)*g->vexnum);//初始所有点属于不同的连通分量 for(int i=0;i<g->vexnum;i++)//初始化 root[i]=-1; int j=0,i=0; //j表示查找第几条边,i表示顶点 while(j<g->edgenum && i<g->vexnum-1){//MST边数为顶点数-1 int svex=findRoot(g,root,edges[j].s); int tvex=findRoot(g,root,edges[j].t); if(svex != tvex){//如果两顶点属于不同的连通分量 root[tvex]=svex; i++; printf("<%c,%c> ",edges[j].s,edges[j].t); } j++; } free(root); delete[] edges; printf("\n"); } void relaxation_Dijkstra(Graph g,unsigned int *dist,char** path,char svex,char tvex){ int i=findVex(g,svex); int j=findVex(g,tvex); assert(j!=-1 && i!=-1); for(int k=0;k<g->alist[j]->size;k++){ char tvex_pos=findVex(g,g->alist[j]->list[k].vex); assert(tvex_pos!=-1); if((g->alist[j]->list[k].weight+dist[j])< dist[tvex_pos]){ dist[tvex_pos]=dist[tvex_pos]+g->alist[j]->list[k].weight; int w; for(w=0;path[tvex_pos][w]!=-1;w++)//更新路径 path[tvex_pos][w]=path[j][w]; path[tvex_pos][w]=g->alist[j]->list[k].vex; } } } //sp[vexnum]标记源点到该点是否已是最短路径 int findMin_Dijkstra(unsigned int dist[],int length,bool sp[]){ unsigned int min=INFINITY; int pos=-1; for(int i=0;i<length;i++){ if(sp[i]==false && dist[i]<min){ min=dist[i]; pos=i; } } return pos; } //最短路径 void ShortestPath_Dijkstra(Graph g,char source_vex){ //最短路径二维数组,sp_path[j]数组保存顶点j的最短路径上的点 char **sp_path=(char**)malloc(sizeof(char*)*g->vexnum); for(int i=0;i<g->vexnum;i++){ sp_path[i]=(char*)malloc(sizeof(char)*g->vexnum); memset(sp_path[i],-1,g->vexnum); } //起始点到其他顶点的距离 unsigned int *sp_dist=(unsigned int*)malloc(sizeof(unsigned int)*g->vexnum); memset(sp_dist,0xFF,sizeof(unsigned int)*g->vexnum); bool *sp_visited=(bool*)malloc(sizeof(bool)*g->vexnum);//标记是否已是最短 memset(sp_visited,0,sizeof(bool)*g->vexnum); int j=findVex(g,source_vex); assert(j!=-1); sp_dist[j]=0; sp_visited[j]=true; relaxation_Dijkstra(g,sp_dist,sp_path,source_vex,source_vex); int rounds=1; while(rounds<g->vexnum){ j=findMin_Dijkstra(sp_dist,g->vexnum,sp_visited); assert(j!=-1); sp_visited[j]=true; rounds++; relaxation_Dijkstra(g,sp_dist,sp_path,source_vex,g->alist[j]->vexname); } for(int i=0;i<g->vexnum;i++){ printf("Shortest Path from %c to %c :%c",source_vex,g->alist[i]->vexname,source_vex); for(j=0;sp_path[i][j]!=-1;j++) printf("%c",sp_path[i][j]); printf("\n",g->alist[i]->vexname); } for(int i=0;i<g->vexnum;i++) free(sp_path[i]); free(sp_path); free(sp_dist); free(sp_visited); } //若P[v][w][u]为TRUE,则u 是从v 到w 当前求得的最短路径上的顶点 const int MAX = 10; bool P[MAX][MAX][MAX]; unsigned int D[MAX][MAX]; void ShortestPath_Flyod(Graph g){ /* 使用一个n*n的方阵D,D[s][t]表示<s,t>的最短路径 * 但是为了D[s][t],需要更新n次D矩阵 * D(k)[s][t]表示经过k次更新后,当前<s,t>的最短路径,可能最终不是 * D(-1)[s][t]=edges[s][t] * D(k)[s][t]=min{ D(k-1)[s][t],D(k-1)[s][k]+D(k-1)[k][t] } * D(k)的计算是尝试把顶点k加到每对顶点<s,t>之间 */ memset(D,0xFF,sizeof(unsigned int)*MAX*MAX); for(int i=0;i<g->vexnum;++i){//初始化D矩阵 for(int t=0;t<g->alist[i]->size;++t){ int tvex_pos=findVex(g,g->alist[i]->list[t].vex); assert(tvex_pos!=-1); D[i][tvex_pos]=g->alist[i]->list[t].weight; //D(-1) for(int u=0;u<g->vexnum;++u) P[i][tvex_pos][u]=false; if (D[i][tvex_pos]<INFINITY){ /*从v 到w 有直接路径*/ P[i][tvex_pos][i]=true; P[i][tvex_pos][tvex_pos]=true; } } } for(int k=0; k<g->vexnum; ++k){//计算D(k),总共n次,这个循环一定要在最外层 for(int s=0; s<g->vexnum; ++s){//D(k)[s] for(int t=0;t<g->alist[s]->size;++t){//D(k)[s][t] int tvex_pos=findVex(g,g->alist[s]->list[t].vex); assert(tvex_pos!=-1); if(D[s][k]<INFINITY && D[k][tvex_pos]<INFINITY && (D[s][k]+D[k][tvex_pos])<D[s][tvex_pos]){ //如果顶点k属于<s,t>最短路径上 D[s][tvex_pos]=D[s][k]+D[k][tvex_pos]; //更新P[s][t],当前P[s][t]最短路径上有哪些顶点 for(int i=0;i<g->vexnum;++i) P[s][tvex_pos][i]=P[s][k][i] || P[k][tvex_pos][i]; } } } } //输入每对顶点的最短路径上的顶点 for(int s=0;s<g->vexnum;s++) for(int t=0;t<g->alist[s]->size;++t){ printf("The Shortest Path Vertexes of <%c,%c> are:\n",g->alist[s]->vexname,g->alist[s]->list[t].vex); int tvex_pos=findVex(g,g->alist[s]->list[t].vex); assert(tvex_pos!=-1); for(int i=0;i<g->vexnum;i++) if(P[s][tvex_pos][i]) printf("%c",g->alist[i]->vexname); printf("\n"); } } int _tmain(int argc, _TCHAR* argv[]) { Graph g; int i; int j; g = graph_create(TEST_SIZE); assert(graph_vertex_count(g) == TEST_SIZE); /* check it's empty */ for(i = 0; i < TEST_SIZE; i++) { for(j = 0; j < TEST_SIZE; j++) { assert(graph_has_edge(g, g->alist[i]->vexname, g->alist[j]->vexname) == 0); } } /* check it's empty again */ for(i = 0; i < TEST_SIZE; i++) { assert(graph_out_degree(g, g->alist[i]->vexname) == 0); graph_foreach(g, g->alist[i]->vexname, match_sink, 0); } /* check edge count */ assert(graph_edge_count(g) == 0); //添加边<u,v>,if u<v unsigned int weight=0;//权值 for(i = 0; i < TEST_SIZE; i++) { for(j = 0; j < TEST_SIZE; j++) { weight = rand()%100 +1; if(i < j) graph_add_edge(g, g->alist[i]->vexname, g->alist[j]->vexname,weight); } } for(i = 0; i < TEST_SIZE; i++) { for(j = 0; j < TEST_SIZE; j++) { assert(graph_has_edge(g, i+'A', j+'A') == (i < j)); } } assert(graph_edge_count(g) == (TEST_SIZE*(TEST_SIZE-1)/2)); //打印图 graph2dot(g); MST_Prim(g,'A'); MST_Kruscal(g); ShortestPath_Dijkstra(g,'A'); ShortestPath_Flyod(g); /* free it * */ graph_destroy(g); return 0; }