前言:
经过一番折腾,最后还是留在了雷达所,不知道是对是错,证明自己的代价就是:别人觉得你行,然后你就得担起责任来了。虽然这些事情你一点也不想干。
不说这些伤心事了,说了项目也得干,做完了再说。还是来看看今天我要做的算法吧。
我的github:
我实现的代码全部贴在我的github中,欢迎大家去参观。
https://github.com/YinWenAtBIT
介绍:
深度优先搜索:
一、定义:
深度优先搜索可以看成是先序遍历的一种推广,递归遍历所有与顶点相邻的点。
编码实现:
深度优先搜索这一部分非常的简单,可以直接介绍编码实现了。
Table deepFirstSearch(Graph G, VertexType start) { Index P1; P1 = FindKey(start, G); if(G->TheCells[P1].Info != Legitimate) return NULL; Table T = (Table)malloc(G->vertex * sizeof(struct TableNode)); initTable(P1, G->vertex, T); Dfs(P1, T, G); return T; } void Dfs(Index S, Table T, Graph G) { Index adj; Edge edge = G->TheCells[S].next; T[S].known = true; while(edge != NULL) { adj = edge->vertexIndex; if(!T[adj].known) { T[adj].path = S; T[adj].dist = edge->weight; Dfs(adj, T, G); } edge = edge->next; } }深度优先搜索很容易就实现了,问题是拿它来干什么。
双连通性:
1.定义:
如果一个连通的无向图中的任意一个顶点被删除之后,剩下的图仍然是连通的,那么这样的无向连通图就称为双连通的。如果一个图不是双连通的,那么,将其删除后图不再连通的那么顶点叫做割点。
运用实例:
一个计算机网络之中,节点是计算机,边是链路。如果有台计算机出故障,那么整个网络并不受影响。
如上图,这样的无向图是双连通的,删除其中任意一个顶点,其他顶点还是连通的。
这样的图就是具有割点的图,删除了顶点C,D,图将会变得不连通。
那么现在的问题就是,如何在一个非双连通的无向图中找出割点。
寻找割点:
使用深度优先算法可以用线性时间找出割点。做法如下:
准备工作:
1.执行深度优先搜素,并且在每个顶点被访问的时候给其编号,称其编号为Num(v);
2.计算编号最低的顶点,成为Low(v)(该点从v开始,通过深度优先生成树的零条或多条边,最多可以由一条反向边)
具体Low(v)计算如下图所示:
上图从点A开始进行深度优先搜索,所示为深度优先搜索生成树。前一个为Num(v),后一个数字为Low(v)。
根据Low(v)的定义可知,Low(v)是:
1.Num(v);
2.所有背向边(v,w)中的最低Num(v);
3.树所有边(v,w)中的最低Low(v)。
前两种方法很明显,第三种方法需要使用递归的方式来说明,因为需要知道顶点v所有儿子的Low值之后才可以算v点的Low值,那么这是一个后序遍历。
判断割点:
1.深度优先遍历树的根,当根多与一个儿子的时候,他才是割点;
2.对于其他顶点v,只有在它的儿子w有Low(w)>=Num(v)时,才是割点(注意,这个条件在根节点处肯定满足,所以需要把根节点单独提取出来)。
编码实现:
有两种方式来实现这个过程,直观的方法:
1.先进行一次深度优先搜索,给每个顶点标上Num值。
2.再进行一次深度优先搜索,计算Low值,同时比较Low值与Num值寻找割点。
这种方法遍历了两次,有更简单的办法:
不存在一个遍历一定是先序遍历或者是后续遍历。在递归调用前处理的部分可以归为先序部分,递归调用后的部分可以归为后续部分,这样,就可以将上面两个过程融合起来。
伪代码:
void FindArt(Vertex V) { Vertex W; Visited[V] = true; Low[V] = Num[V] = Counter++; for each W adjacent to V { if(!Visited[W]) { Parent[W] = V; FindArt(W); if(Low[W]>= Num[V]) printf("%v is an articulation point\n", v); Lov[V] = Min(Low[V], Low[W]); } else if(Parent[V] != W) Low[V] = Min(Low[V], Num[W]); } }
struct ArtNode { bool known; int parent; int Low; int Num; };启动函数:
void FindArticulation(VertexType S, Graph G) { Index P1; P1 = FindKey(S, G); if(G->TheCells[P1].Info != Legitimate) return; int count =1; struct ArtNode * Art= (struct ArtNode *)calloc(G->vertex, sizeof(struct ArtNode)); FindArt(P1, G, Art, &count); free(Art); }运算函数:
void FindArt(Index S, Graph G, struct ArtNode * Art, int * countPos) { Index W; int rootchild =0; Art[S].known = true; Art[S].Low = Art[S].Num = (*countPos)++; Edge edge = G->TheCells[S].next; while(edge != NULL) { W = edge->vertexIndex; /*正向边*/ if(!Art[W].known) { if(Art[S].Num ==1) { rootchild++; if(rootchild >1) printf("%s is a articulation point\n", G->TheCells[S].vertexName); } Art[W].parent = S; FindArt(W, G, Art, countPos); /*正向边*/ /*根处总是满足这个条件,需要排除*/ if(Art[W].Low >= Art[S].Num && Art[S].Num !=1) printf("%s is a articulation point\n", G->TheCells[S].vertexName); Art[S].Low = Min(Art[W].Low, Art[S].Low); } else if(Art[S].parent != W) Art[S].Low = Min(Art[W].Num, Art[S].Low); edge = edge->next; } }测试结果:
第一段输出显示的是深度优先搜索的路径,这是一张路径表,第二部分输出的就是对割点的判断。判断正确。
总结:
这一部分实现起来还是挺容易的,感觉在做完了360的笔试题目之后,已经在创业公司编写了几天代码之后,整个编码速度再一次上升了。编写完毕,测试也很快就通过了。后一部分的欧拉排序也挺简单就看懂了,只不过一直没想通怎样才能做到O(E+V)的复杂度。这个可能还需要再考虑考虑。