带权图 GraphW
最小生成树(MSTW)
有权图的最小生成树是用优先级队列来实现的,优先级队列 可以基于堆来实现,也可以基于数组实现。
方法
- 从一个顶点开始,放到树的集合中,循环执行下面步骤
- 找到从最新的顶点到其他顶点的的所有边,这些顶点不在树的集合中,将这些边放到优先级队列
- 找到权值最小的边,把它和它所到达的顶点都放到树的集合中
顶点分类
- 已经被连接的顶点,它们是最小生成树的顶点
- 还没有被链接的顶点,但是知道把他们连接到至少一个已知顶点的权重,
这些叫做边缘顶点 - 还不知道任何信息的顶点
边的剪除
在算法中,要确保优先级队列不能有连接在已在树中的的顶点的边,每次向树中增加顶点后,都要遍历优先级队列查找并删除这样的边,也就是优先级队列中应该只包含一条到达某个第二类顶点的边
putInPQ
:保证优先级队列中应该只包含一条到达某个特定目标顶点的边
- 调用
find()
方法,寻找优先队列中是否存在可以到达指定节点的边,
如果存在,返回其所在队列的位置,不存在则返回-1
- 存在:如果
oldDist
大于newDist
,则在队列中删除removeN
前者所在tempEdge
,插入insert
后者theEdge
;
如果oldDist
小于或等于newDist
,则队列不作更改 - 不存在:直接
insert
插入Edge
算法具体实现
- 当前顶点
currentVert
放在树中 - 连接这个顶点的边放到优先级队列中
putInPQ
,但是只要一下任意一个条件满足,这条边就不可以放入队列中
- 源点和终点相同
- 终点在树中
- 源点和终点之间没有边(邻接矩阵中对应的值为
INFINITY
)
3.将权值最小的边从优先级队列中删除removeMin
,把这条边和该边的终点加入树,并显示源点和终点。
代码片段
public void mstw(){
currentVert=0;
while (nTree<nVerts){
vertexList[currentVert].isInTree=true;
nTree++;
for(int j=0;j<nVerts;j++){
if(j==currentVert)
continue;
if(vertexList[j].isInTree)
continue;
int distance=adjMat[currentVert][j];
if(distance==INFINITY)
continue;
putInPQ(j,distance);
}
if(thePQ.size()==0){
System.out.println("GRAPH NOT CONNECTED");
return;
}
Edge theEdge=thePQ.removeMin();
int sourceVert=theEdge.srcVert;
currentVert=theEdge.destVert;
System.out.print(vertexList[sourceVert].label);
System.out.print("->");
System.out.print(vertexList[currentVert].label);
System.out.print(" ");
}
for(int j=0;j<nVerts;j++)
vertexList[j].isInTree=false;
}
public void putInPQ(int newVert,int newDist){
int queueIndex=thePQ.find(newVert);
if(queueIndex!=-1){
Edge tempEdge=thePQ.peekN(queueIndex);
int oldDist=tempEdge.distance;
if(oldDist>newDist){
thePQ.removeN(queueIndex);
Edge theEdge=new Edge(currentVert,newVert,newDist);
thePQ.insert(theEdge);
}
}else{
Edge theEdge=new Edge(currentVert,newVert,newDist);
thePQ.insert(theEdge);
}
}
最短路径(Dijkstra)
最短路径:寻找带权图中给定某两点间的最短路径(代码基于有向带权图)。
方法
- 每次将一个新顶点放入树,用这个新顶点不断修正记录,在记录中只保留从源顶点到某个已知顶点的最低费用
- 不断将新的顶点放入树,条件是从源点到那个城市的路线费用最低
顶点分类
- 已经被连接的顶点,它们是已在树中
- 还没有被链接的顶点,但是知道把他们连接到至少一个已知顶点的权重,这些叫做边缘顶点
- 还不知道任何信息的顶点
算法步骤
- 将源顶点放入树中,并记录此顶点到其他可直达顶点的权值,不可直达为
INFINITY
- 取权值最小的顶点,并分别记录源顶点到其他顶点(源顶点直达+源顶点经此顶点到达)的最小权值
- 确定第二个顶点,将其放入树中
- 循环2到3
- 到达目标顶点,结束
算法解析
sPath[]
数组存储从源点开始到其他顶点(终点)的最短距离。不仅记录从源顶点到每个终点的最短路径,还应该记录走过的路径,即记录终点的父节点
DisPar
。- C->E[140]:
C
是起始顶点,E
终止顶点,[150]
表示从源节点到E
的最短路径的权值和,C->E
表示路径
- C->E[140]:
path()
选择源顶点放入树中,并记录sPath
,开始循环- 选择
sPath[]
数组的最小距离 - 把对应的顶点放入树中,这个顶点变成新顶点
currentVert
- 根据
currentVert
变化,更新adjust_sPath()
所有的sPath[]
数组内容
- 选择
adjust_sPath()
当例程被调用的时候,currentVert
被放入树中,startToCurrent
是sPath[]
数组中的当前项。
adjust_sPath()
方法现在检测sPath[]
数组的每一项,它使用循环计数器column
依次指向每一个顶点,对于sPath[]
数组的每一项,如果顶点不在树中,就做以下三件事:- 把当前顶点的距离
startToCurrent
加到从源顶点到当前顶点的距离上startToFringe
- 把
startToFringe
与sPath[]
数组当前项做做比较 - 如果
startToFringe
更小,就替换当前项
- 把当前顶点的距离
代码片段
public void path(){
int startTree=0;
vertexList[startTree].isInTree=true;
nTree=1;
for(int j=0;j<nVerts;j++){
int tempDist=adjMat[startTree][j];
sPath[j]=new DisPar(startTree,tempDist);
}
while (nTree<nVerts){
int indexMin=getMin();
int minDist=sPath[indexMin].distance;
if(minDist==INFINITY){
System.out.println("There are unreachable vertices");
break;
}else{
currentVert=indexMin;
startToCurreent=sPath[indexMin].distance;
}
vertexList[currentVert].isInTree=true;
nTree++;
adjust_sPath();
}
displayPaths();
nTree=0;
for(int j=0;j<nVerts;j++)
vertexList[j].isInTree=false;
}
public void adjust_sPath(){
int column=1;
while (column<nVerts){
if(vertexList[column].isInTree){
column++;
continue;
}
int currentToFringe=adjMat[currentVert][column];
int startToFringe=startToCurreent+currentToFringe;
int sPathDist=sPath[column].distance;
if(startToFringe<sPathDist){
sPath[column].parentVert=currentVert;
sPath[column].distance=startToFringe;
}
column++;
}
}
Github
完整代码,我托管在Github上,如果对你有帮助,请给我点个star以示肯定和鼓励。