贪心算法初探3——最短路径(Dijkstra算法)

  问题描述:给定有向带权图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[]逆向找到最短路径上经过的地点。

  算法实现:

  

 1 private static final int MAX_Number=1000000;//定义静态常量用于表示无穷大
 2 public int[] Solution4(int u){
 3             int[][] ints=new int[][]{{MAX_Number,2,5,MAX_Number,MAX_Number},
 4                                 {MAX_Number,MAX_Number,2,6,MAX_Number,},
 5                                 {MAX_Number,MAX_Number,MAX_Number,7,1},
 6                                 {MAX_Number,MAX_Number,2,MAX_Number,4},
 7                                 {MAX_Number,MAX_Number,MAX_Number,MAX_Number,MAX_Number}};//初始邻接矩阵,选择点1作为源点。
 8             int[] dist=new int[]{0,2,5,MAX_Number,MAX_Number};//记录源点到i顶点的最短路径长度
 9             int[] p=new int[]{-1,1,1,-1,-1};//记录前驱顶点
10             boolean[] S=new boolean[]{true,false,false,false,false};//如果i顶点已经加入S,S[i]的值会是true,否则就是false
11             for (int j=1;j<dist.length;j++){
12                 int temp=MAX_Number;//中间变量
13                 int t=u;
14                 for(int i=0;i<dist.length;i++){
15                     if(!S[i] && dist[i]<temp){
16                         t=i+1;
17                         temp=dist[i];
18                     }
19                     if(i==dist.length-1){
20                         if(t!=u){
21                             S[t-1]=true;
22                         }
23                     }
24                 }
25                 for(int k=0;k<dist.length;k++){
26                     if(!S[k]&&dist[k]>dist[t-1]+ints[t-1][k]){
27                         dist[k]=dist[t-1]+ints[t-1][k];
28                         p[k]=t;
29                     }
30                 } }
31             return dist;
32     }//单源最短路径(Dijkstra)

  返回值[0,2,4,8,5],时间复杂度O(n2)> 

  可以再尝试使用栈来获取每次更新dict数组时的路径,最终也返回源点到每个点的最短路径。之后会继续加上最短路径并且优化算法使时间复杂度降至O(logn)。

  (我为什么这么菜,为什么这么菜)

猜你喜欢

转载自www.cnblogs.com/jason-Ralston/p/11944877.html