风吹夏天 2019-12-07
问题描述:给定有向带权图G=(V,E),其中每条边的权是非负实数。此外,给定V中的一个顶点,称为源点。现在要计算从源点到所有其他各顶点的最短路径长度,这里路径长度指路上各边的权之和。
算法设计:这个问题一般采用迪杰斯特拉算法(Dijkstra)算法思想是先求出长度最短的一条路径,再参照该最短路径求出长度次短的一条路径,直到求出从源点到其他各个顶点的最短路径。
算法基本思想:先假定源点为u,顶点集合V被划分为两部分:集合V以及集合V-S。初始时S中仅含有u,其中S的顶点到源点的最短路径已经确定。集合V-S中华包含的顶点到源点的最短路径长度待定,称从源点出发只经过S中的点到V-S中点的路径为特殊路径,并用数组dict[]记录当前每个顶点所对应的最短特殊路径长度。
贪心策略选择:选择特殊路径长度最短的路径,将其连接的V-S顶点加入到集合S中,同时更新数组dict[].一旦S包含了所有的顶点,dict[]就储存了从源到所有其他顶点之间的最短路径长度。
算法详情:
1:设置地图带权邻接矩阵为map[][],如果从源点u到顶点i有边,就map[u][i]等于<u,i>的权值,否则map[u][i]=∞;使用一维数组dict[i]记录从源点到i顶点的最短路径长度;使用一维数组p[i]记录最短路径上i顶点的前驱。
2:设置一个集合S={u},对于集合V-S中所有顶点x,初始化dist[i]=map[u][i],如果u与i有边项链,初始化p[i]=u,否则p[i]=-1。
3:在集合V-S中依照贪心策略寻找使得dist[j]具有最小值的的顶点t,即dist[t]=min(dist[j]j属于V-S),则顶点t就是集合V-S中距离源点u最近的顶点。
4:将t加入S中,同时更新V-S。
5:如果集合V-S已经为空,算法结束,否则转入第6步。
6:在第三步中已经找到了源点到t的最短路径,那么对于集合V-S中所有与顶点t相邻的顶点j都可以通过t走捷径。如果dist[j]>dist[t]+map[t][j],则dist[j]=dist[t]+map[t][j],同时记录顶点j的前驱为t,有p[j]=t,转入第3步。
由此,可以求得源点u到图G的其余各个顶点的最短路径及长度,也可以通过数组p[]逆向找到最短路径上经过的地点。
算法实现:
private static final int MAX_Number=1000000;//定义静态常量用于表示无穷大 public int[] Solution4(int u){ int[][] ints=new int[][]{{MAX_Number,2,5,MAX_Number,MAX_Number}, {MAX_Number,MAX_Number,2,6,MAX_Number,}, {MAX_Number,MAX_Number,MAX_Number,7,1}, {MAX_Number,MAX_Number,2,MAX_Number,4}, {MAX_Number,MAX_Number,MAX_Number,MAX_Number,MAX_Number}};//初始邻接矩阵,选择点1作为源点。 int[] dist=new int[]{0,2,5,MAX_Number,MAX_Number};//记录源点到i顶点的最短路径长度 int[] p=new int[]{-1,1,1,-1,-1};//记录前驱顶点 boolean[] S=new boolean[]{true,false,false,false,false};//如果i顶点已经加入S,S[i]的值会是true,否则就是false for (int j=1;j<dist.length;j++){ int temp=MAX_Number;//中间变量 int t=u; for(int i=0;i<dist.length;i++){ if(!S[i] && dist[i]<temp){ t=i+1; temp=dist[i]; } if(i==dist.length-1){ if(t!=u){ S[t-1]=true; } } } for(int k=0;k<dist.length;k++){ if(!S[k]&&dist[k]>dist[t-1]+ints[t-1][k]){ dist[k]=dist[t-1]+ints[t-1][k]; p[k]=t; } } } return dist; }//单源最短路径(Dijkstra)
返回值[0,2,4,8,5],时间复杂度O(n2)>
可以再尝试使用栈来获取每次更新dict数组时的路径,最终也返回源点到每个点的最短路径。之后会继续加上最短路径并且优化算法使时间复杂度降至O(logn)。
(我为什么这么菜,为什么这么菜)