读书人

图的底层兑现

发布时间: 2013-03-01 18:33:02 作者: rapoo

图的底层实现
1.图数据结构出现的背景

上面几篇内容都是关于树,树在表示层次关系的数据类型时很适合,但是也仅限于“parent-child”的表示,兄弟节点等其他关系只能间接的表示,而图“graph”就能很好的弥补这个缺陷。

图的应用也很广泛,我也遇到过,例如网络中的路由器在路径的选择上就是根据最小路径的算法;在进行用户行为分析时,也可以将用户的行为轨迹对应成图,进而对图进行分析,例如有哪些功能并没有使用,在哪个位置遇到了问题等等。

该篇文章主要介绍图的底层实现。

2.图的表示

图的表示方式有很多,有两种比较典型数据结构:邻接表和矩阵。

2.1邻接表

对于每个顶点,如果有与其他节点连接,则依次用链表的形式将其他顶点放在该顶点后,如下图所示

图的底层兑现

2.2邻接矩阵

矩阵的大小等于顶点数*顶点数,如有顶点a,b连接,则用1表示;否则为0

图的底层兑现

其代码表示如下,主要需要一个矩阵记录顶点是否连接。

  //最小路径Dijkstra算法,一般是在有向图中    public voidDijkstra(int first){       //把所有节点的值都设置成无限大       for(int i=0; i<maxVertices;i++){           currentDist[i] =10000000;//每个节点的currentDist的值设置成无限大       }       currentDist[first]= 0;           for(int i =0; i<maxVertices; i++){           toBeChecked.add(newInteger(i));       }       while(!toBeChecked.isEmpty()){           intminPosition = minDist();           toBeChecked.remove(minPosition);//把最小权值的节点移出toBeChecked           for(int i =0; i<toBeChecked.size();i++){              intposition = toBeChecked.get(i);//获得节点              intdistance = matrix[minPosition][position];//获得的节点与最小节点间的距离              //满足条件则说明distance不是无限大,即有连接              if(currentDist[position]>currentDist[minPosition]+distance){                  currentDist[position]=currentDist[minPosition]+distance;              }           }         }    }    //找出toBeChecked中拥有当前权值最小的点    public intminDist(){       intminPosition = 0;       for(int i=1; i<toBeChecked.size();i++){           if(currentDist[i]<minPosition)              minPosition = i;       }       returnminPosition;    }

缺点:Dijkstra算法不适合负数的情况,可能导致某条边没考虑进去

4.2FordAlgorithm

为了弥补Dijkstra算法的缺点,提出了FordAlgorithm,在Dijkstra的基础上进行优化的:直到处理完了整个图才最终决定每个点的最短路径。为了方便起见,写出了该算法的伪码。

FordAlgorithm(diagraph, vertex first)

forall vertices v

currentDist(V) = 无穷大;

currentDist(first)= 0;

whilethere is an edge(VU) such that currentDist(U) >currentDist(V)+weight(edge(VU))

currentDist(U)= currentDist(V)+weight(edge(VU))

缺点:在每次循环中要检查每一条边,这不是很有必要,于是有了下面的改进算法。

4.3LabelCorrectingAlgorithm

该算法中不需要每次都检查每条边,而是距离发生了改变才检查。

labelCorrectingAlgorithm(diagraph,vertex first)

forall vertices v

currentDist(V)= 无穷大;

currentDist(first)= 0;

toBeChecked= {first};

while(toBeCheckedis not empty)

v= a vertex in toBeChecked;

removev from toBeChecked;

forall vertices u adjacent to V

ifcurrentDist(u) > currentDist(v) +dist(uv)

currentDist(u)= currentDist(v) + dist(uv)

add u to toBeChecked;

看最后一句,只有某个节点的距离发生了改变后,才会重新检查该节点的邻接节点。

那么怎么管理toBeChecked呢?一种方法是用队列,将距离发生改变的节点一次放入队列中,但是会造成问题:可能会有节点重复出现在队列中(例如c->d,c->g,g->d)。解决方案:将第一次放入toBeChecke中的节点放在末尾,避免第一次放入的节点重复计算。

5.Cycle环检测

使用深度优先遍历算法来检测环的回路。

5.1非有向图

cycleDetectionDFS(v)

num(v)= i++;//i的初始值为1

forall vertices u adjacent to v;

ifnum(u) = 0

attachedge(uv) to Edge;

cycleDetectionDFS(u);

elseif edge(uv) not in Edge//即U已经遍历过了,但不是通过uv

cycledetected;

5.2有向图

diagraphCycleDetectionDFS(v)

num(v)= i++;//i的初始值为1

forall vertices u adjacent to v;

ifnum(u) = 0

pred(u) = v;

diagraphCycleDetectionDFS(u);

elseif num(u) is not 无穷大

pred(u)= v;

cycledetected;

num(v)= 无穷大//通过该句,程序只会在最深层(检测到环路)才会执行else if语句

读书人网 >其他相关

热点推荐