·背景
在大部分数据结构的讲义中,图一般出现在第7章。可惜当年没好好学,现在重新拿出来啃一遍。印象中不少老师对于该章节都填鸭式的带过或者摆在最后开讲,也许因为当年LBS并不流行。在其章节后的是一些排序和管理,但就概念复杂度或者封装流行度而言,图还是更难一点。如果仅靠伪代码,需要更长的时间来消化。图,也许就像游戏中的最终BOSS一样,其恐惧、神秘、优雅、传说还是吸引着众多Fans们趋之若鹜。这两天边回忆、边吃饭、边吐血、边洗澡把最常用的一些观点重新笔记下来,但其实也只是冰山一角。
·分解
历经百年,图的构成和运用已经遍布生活,其中欧洲人对于这块的贡献最大。整个图衍生出多个发展方向,如下图所示:
底层的图是一个大概念,如果没什么特别需求,可以理解就是我们的地图,只不过它更原始一点,更苍白一点,就一些线和点。
往上一层,分解出了有向、无向。也就是有没有箭头的区别,单行道、双行道的区别。
往上一层,分解出了权值。也就是这条路好不好走,要花多大代价走这条路。
往上一层,分解出了基于图应用算法。有一些基本的算法其实是通用的,笔者就目前流行的趋势稍微归一下类。
·基本遍历:DFS、BFS。一个按前序遍历,一个按层序遍历。帮助入门,缺点上:凡是遍历过的不再触碰,容易导致变形数。
·最小生成树:Prim、Kruskal。经常会听搞网络的同学说起,当然听到这个词语的时候,但后面两个名词很少听到,其实就是它们。一般来说,听到时候,多数是有人插错网线成环了。
·最短路径:Dijkstra。名字很难念,很多应用的看家法宝。其他还有一些算法就不举例了。缺点上,一般只给了结果,要想知道过程(路由明细),还需要再加工。
·强连通:Tarjen。一个找强连通的分量的算法,两两互联称为强连通。但具体能干什么用,还没想通,分类?机学?后续待研究。
·图匹配:匈牙利算法:二分图的最大匹配常用算法,同上,后续待研究。
·前三项算法的举例
原理就不多写了,网上都有,这里就边贴代码边分析。
·DFS:
1 #include <iostream> 2 #include <sstream> 3 #include <fstream> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <vector> 7 #include <typeinfo> 8 #include <set> 9 #include <stack> 10 11 12 using namespace std; 13 14 typedef string VertexType; //顶点类型应由用户定义 15 typedef int EdgeType; //边上的权值类型应由用户定义 16 typedef int Boolean; 17 18 //最大顶点数,应由用户定义 19 #define INF -1 //用-1来代表无穷大 20 #define DEBUG 21 #define TRUE 1 22 #define FALSE 0 23 24 25 typedef struct 26 { 27 VertexType vexs[5]; //顶点表 28 EdgeType arc[5][5]; //邻接矩阵,可看作边 29 int numVertexes, numEdges; //图中当前的顶点数和边数 30 } Graph; 31 32 typedef struct 33 { 34 char d1[2]; 35 char d2[2]; 36 int wv; 37 } Wing; 38 39 typedef struct 40 { 41 vector<string> v1; 42 vector<string> v2; 43 vector<int> v3; 44 } Route; 45 46 Boolean visited[5]; //访问标志数组 47 Route *r; //路由表 48 int rp=0; //路由表长度 49 stack<string> minroute; //最短路径栈 50 51 52 int locates(Graph *g, string sch); 53 void init(Graph *g); 54 void printGraph(Graph g); 55 void CreateGraph(Graph *g,Wing *ww,vector<string> &v); 56 void DFS(Graph g, int i,vector<string> &v); 57 void DFSclear(Graph g,vector<string> &v); 58 void DFSR(Graph g, int i, vector<string> &v,Route *r,stack<string> &stk); 59 60 61 int main() 62 { 63 //放原始图 64 Wing *w=NULL; 65 w=(Wing*)malloc(sizeof(Wing)*6); 66 67 68 //读原始图 69 std::ios::sync_with_stdio(false); 70 string line; 71 string filename="./tu004.cfg"; 72 ifstream ifs; 73 ifs.open(filename.c_str()); 74 int i=0; 75 set<string> v2; 76 vector<string> v1; //遍历线序 77 while(getline(ifs,line)) 78 { 79 istringstream strstm(line); 80 string str1,str2,str3; 81 strstm>>str1>>str2>>str3; 82 strcpy((w+i)->d1,str1.c_str()); 83 strcpy((w+i)->d2,str2.c_str()); 84 (w+i)->wv=atoi(str3.c_str()); 85 v2.insert(str1); 86 v2.insert(str2); 87 i++; 88 } 89 90 //邻接矩阵创建图和顶点数组枚举创建 91 Graph g; 92 93 set<string>::iterator v2_it; 94 for(v2_it=v2.begin();v2_it!=v2.end();v2_it++) 95 { 96 v1.push_back(*v2_it); 97 } 98 99 //设置顶点和边上限 100 g.numVertexes=5; 101 g.numEdges=6; 102 103 //开始干活 104 init(&g); 105 CreateGraph(&g,w,v1); 106 printGraph(g); 107 108 //结束 109 free(w); 110 111 //DFS 深度优先全遍历 112 /* 113 for(int i=0;i<5;i++) 114 { 115 DFSclear(g,v1); 116 DFS(g,i,v1); 117 for(int j=0;j<5;j++) 118 cout<<v1[j]<<" "; 119 cout<<"---------------------------------------------------------------------"<<endl; 120 121 } 122 */ 123 124 //DFS 深度优先,输出路由表 125 r=(Route*)malloc(sizeof(Route)*6); 126 DFSclear(g,v1); 127 DFSR(g,2,v1,r,minroute); 128 for(int j=0;j<5;j++) 129 cout<<v1[j]<<" "; 130 cout<<"\n---------------------------------------------------------------------"<<endl; 131 132 //打印路由表 133 for(int j=0;j<rp;j++) 134 cout<<r->v1[j]<<":"<<r->v2[j]<<":"<<r->v3[j]<<endl; 135 136 cout<<"\n---------------------------------------------------------------------"<<endl; 137 138 //打印最短路径 139 while(minroute.size()>0) 140 { 141 cout<<minroute.top()<<endl; 142 minroute.pop(); 143 } 144 } 145 146 147 void DFSR(Graph g, int i, vector<string> &v,Route *r, stack<string> &stk) 148 { 149 cout<<"进入遍历中"<<i<<":"; 150 stk.push(g.vexs[i]); 151 v.push_back(g.vexs[i]); 152 int j; 153 visited[i] = TRUE; 154 cout<<" 顶点:"<<g.vexs[i]<<endl; 155 for(j = 0; j < g.numVertexes; j++) 156 { 157 cout<<"\t遍历点:"<<j<<"的状态"<<visited[j]<<" "<<"g.arc的值"<<g.arc[i][j]<<endl; 158 if(g.arc[i][j] == 1 && !visited[j]) 159 { 160 cout<<"\t\t\t\t 准备进入下一点:"<<j<<endl; 161 cout<<"\t\t\t\t\t\t "<<i<<":"<<j<<":"<<g.arc[i][j]<<endl; 162 r->v1.push_back(g.vexs[i]); 163 r->v2.push_back(g.vexs[j]); 164 r->v3.push_back(g.arc[i][j]); 165 rp++; 166 DFSR(g,j,v,r,stk); //对为访问的邻接顶点递归调用 167 } 168 } 169 if(stk.top()=="3") 170 { 171 cout<<"完成最短路径"<<endl; 172 } 173 else 174 { 175 stk.pop(); 176 } 177 cout<<"结束本次"<<i<<"遍历"<<endl; 178 } 179 180 181 182 void DFS(Graph g, int i, vector<string> &v) 183 { 184 cout<<"进入遍历中"<<i<<":"; 185 v.push_back(g.vexs[i]); 186 int j; 187 visited[i] = TRUE; 188 cout<<" 顶点:"<<g.vexs[i]<<endl; 189 //printf("%s ", g.vexs[i].c_str()); //打印顶点,也可以其他操作 190 for(j = 0; j < g.numVertexes; j++) 191 { 192 cout<<"\t遍历点:"<<j<<"的状态"<<visited[j]<<" "<<"g.arc的值"<<g.arc[i][j]<<endl; 193 if(g.arc[i][j] == 1 && !visited[j]) 194 { 195 cout<<"\t\t\t\t 准备进入下一点:"<<j<<endl; 196 cout<<"\t\t\t\t\t\t "<<i<<":"<<j<<":"<<g.arc[i][j]<<endl; 197 DFS(g,j,v); //对为访问的邻接顶点递归调用 198 } 199 } 200 cout<<"结束本次"<<i<<"遍历"<<endl; 201 } 202 203 204 205 206 207 void DFSclear(Graph g,vector<string> &v) 208 { 209 int i; 210 cout<<endl; 211 cout<<"初始化所有顶点状态都是未访问过状态"<<endl; 212 for(i = 0; i < g.numVertexes; i++) 213 { 214 visited[i] = FALSE; //初始化所有顶点状态都是未访问过状态 215 } 216 v.clear(); 217 } 218 219 220 221 222 void CreateGraph(Graph *g,Wing *ww,vector<string> &v) 223 { 224 printf("刚才输入顶点数和边数为:%d %d\n", g->numVertexes, g->numEdges); 225 //设置顶点数组 226 for(int i=0;i<g->numVertexes;i++) 227 { 228 g->vexs[i]=v[i]; 229 cout<<g->vexs[i]<<" "; 230 //printf("%s ",g->vexs[i].c_str()); 231 } 232 cout<<endl; 233 234 235 //矩阵赋值 236 for(int k=0;k<6;k++) 237 { 238 int m=-1; 239 int n=-1; 240 m = locates(g,(ww+k)->d1); 241 n = locates(g,(ww+k)->d2); 242 243 if(n == -1 || m == -1) 244 { 245 fprintf(stderr, "there is no this vertex.\n"); 246 return; 247 } 248 //printf("m=%d,n=%d\n",m,n); 249 g->arc[m][n] = (ww+k)->wv; 250 g->arc[n][m] = g->arc[m][n]; 251 } 252 253 } 254 255 void init(Graph *g) 256 { 257 for(int i=0;i<g->numVertexes;i++) 258 for(int j=0;j<g->numVertexes;j++) 259 { 260 g->arc[i][j]=0; 261 } 262 } 263 264 265 266 int locates(Graph *g,string sch) 267 { 268 int i = 0; 269 for(i = 0; i < g->numVertexes; i++) 270 { 271 if(g->vexs[i] == sch) 272 { 273 break; 274 } 275 } 276 if(i >= g->numVertexes) 277 { 278 return -1; 279 } 280 return i; 281 } 282 283 void printGraph(Graph g) 284 { 285 printf("开始打印\n"); 286 int i, j; 287 for(i = 0; i < g.numVertexes; i++) 288 { 289 for(j = 0; j < g.numVertexes; j++) 290 { 291 printf("%d ", g.arc[i][j]); 292 } 293 printf("\n"); 294 } 295 }