您的位置:首页 > 其它

在有向图中找两个点之间最短路径的方法总结

2018-03-21 20:03 381 查看
实验原题如下:





这篇博客中主要讨论在上述情况如何找两点之间的最短路径。
由于上个学期刚接触过类似的问题,所以我首先想到的就是单源求最短路径。Void Dijkstra(GRAPH G, costtype D[MAXVEX+1] )
{ int S[MAXVEX+1] ;
for ( i=1 ; i<=n; i++ )
{ D[i]=G[1][i] ; S[i]=FALSE ; }
S [1]= TRUE ;
for( i=1; i<n; i++)
{ w=mincost ( D, S ) ;
S[w]=TRUE ;
for ( v=2 ; v<= n ; n++ )
if ( S[v]!=TRUE )
{ sum=D[w] + G[w][v] ;
if (sum < D[v] ) D[v] = sum ; }
}
}
int mincost ( D, S )
{
temp = INFINITY ;
w = 2 ;
for ( i=2 ; i<=n ; i++ )
if ( !S[i] && D[i]<temp)
{ temp = D[i] ;
w = i ;
}
return w ;
}算法Dijkstra的原理如下:
它的输入G是一个表示两点之间距离的二维数组,如果两点并非直接相连,则距离为无穷大。
另一个输入D是用于存放该点到其他点之间的最短距离。



当时的想法是尽快达到预期结果,所以使用了更简单的Floyd算法,求任意两点间的最短路径。
输入C是表示两点之间距离的二维数组,输入A用于存放任意两点间的最短路径。
这里需要注意的一点是Floyd算法输入的C,自己到自己的距离应为0,而Dijkstra算法不需要考虑自己到自己的情况。Void Floyd( A , C , n )
{ for ( i = 1; i <= n; i++ )
for ( j = 1; j<=n; j++ )
A[i][j] = C[i][j] ;
for ( k = 1; k <= n; k++ )
for ( i = 1; i <= n; i++ )
for ( j = 1; j <=n; j++ )
if ( A[i][k] + A[k][j] < A[i][j] )
A[i][j] = A[i][k] + A[k][j] ;
}
Floyd算法只需运行一次就能得到全部结果,但是时间复杂度的代价太大,达到了O(n3)。
后来老师讲,现在时间复杂度为O(n2)都不是很好,大于O(n2)的就更看不下去了。
其实实验手册中也提及到,这里最优的算法应该是树的广度优先遍历,即树的层序遍历。使起始点为根结点,两点之间的最短距离就是另一个点在树中的层数-1。但是想使用广度优先遍历寻找最短路径是有条件的,需要默认两点之间的边要么不存在,要么全都等于相等的值。
树的层序遍历算法抽象的伪代码如下Void Level_list_Btree( BTREE T)
{
QUEUE Q;
MAKENULL(Q);
ENQUEUE(T,Q)
while(!EMPTY(Q))
{ P=FRONT(Q);
DEQUEUE(Q);
visite(P->data);
if(P->lchild) ENQUEUE(P->lchild,Q)
if(P->rchild) ENQUEUE(P->rchild,Q);
}
}
它是队列的一种应用,但是怎么得到要找的点在第几层呢?
将点设计为一个类,其中的一个域存放层数,一旦它生成一个孩子结点,就将孩子节点的层数设为父节点的层数+1。public int getDistance(Person person1,Person person2) {
if(!(people.contains(person1)&&people.contains(person2))) {
System.out.println("addEdge输入不在图中的人,程序结束运行");
System.exit(0);}
Set<Person> person_allinqueue = new HashSet<Person>();
person_allinqueue.add(person1);
Queue<Person> queue = new LinkedList<Person>();
queue.offer(person1);
person1.setinitlevel();
while(queue.peek() != null) {
Person father = queue.poll();
if(father == person2) return father.calllevel();
Person[] children= father.callchildren();
if(children.length != 0) {
for(Person child:children) {
if(!person_allinqueue.contains(child)) {
person_allinqueue.add(child);
child.setlevel(father.calllevel()+1);
if(child == person2) return father.calllevel()+1;
queue.offer(child);
}
}
}
}
return -1; }
public class Person {
private String name;
private List<Person> children = new ArrayList<Person>(); ;
private int level;
public void addchild(Person child) {
children.add(child);
}
public Person(String str) {
name = str;
}
public String callname() {
return name;
}
public Person[] callchildren() {
Person[] Children = new Person[children.size()];
for (int i = 0; i < children.size(); i++) {
Children[i] = children.get(i);
}
return Children;
}
public int calllevel() {
return level;
}
public void setinitlevel(){
level = 0;
}
public void setlevel(int level){
this.level = level;
}

}
在有向图中使用树的层序遍历还需要注意的是,不能形成自环,为此设了一个存放所有进过队列的点的Set Set<Person> person_allinqueue = new HashSet<Person>();用它来确保新进入队列的点不是重复的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: