贪心算法初探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[]逆向找到最短路径上经过的地点。

算法实现:

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)。

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

相关推荐