迪杰斯特拉 dijkstra 算法思想+Java实现

求单源最短路径的经典算法,算法基于这样一种思想:
1 将顶点集V分成 S(开始只包含源点, S包含的点都是已经计算出最短路径的点) 和 V-S 集合(V-S 包含那些未确定最短路径的点)

2. 从V-S中选取这样一个顶点w: 满足经过S集合中任意顶点 v到w 的路径最短, 即
满足( 源到v的路径 + v到w的路径) 最小的那个w. 其中v 属于S, w属于S-V。将w 加入S, 并从V-S中移除w.

3. 如此反复,直到V-S变空集为止。


归纳基点:
比如开始 S中只有原点s, 那我们从V-S中选取离s最近的点w, 则s直接到w的路径一定最短。因为如果存在另一个点w2, 使得 s->w2->w 的路径比s 直接到w短,那么s->w2 一定 小于s->w, 那么 w就不是离s最近的点,与我们选取的w离s最近这个先前条件矛盾。所以在最开始,选出来的那个点 w 一定确定了最短路径,即s->w 的边长。 w已经确定,将w加入S.

2. 此时S中有 v1,v2....vk 顶点,V-S中剩下 w1,w2.....Wn-k 顶点。我们此时当然选择离S集合最近的点w.

w必然满足 D[w]= Min(D[v]+D[v to w]) 其中 D[x] 表示从原点到x的最短路径长。

这个w 此时一定取得了最短路径D[w], 因为如果有其他的路径,假设是 S集合某路径->Wi->w, 那么就应当去选取Wi 做为该点而不是w. 因为 S集合某路径长D[v] + D[v to Wi] < D[v]+D[v to w] 与 w满足的公式矛盾。

因此w必然是最小的。将w加入 S集合。


3. 实现框架+核心代码:

采用邻接表存储图


1 /*
2 * 邻接边
3 * */
4 public class Edge {
5 public Vertex dest;//目标顶点
6 public double cost;//代价
7
8 public Edge(Vertex d,double c){
9 dest=d;
10 cost=c;
11 }
12 }
13

顶点类:
1 public class Vertex implements Comparable<Vertex>{
2 public String name;//顶点名
3 public List<Edge> adj;//邻接表
4 public double dist;// 从原点到该点的最短路径长
5 public Vertex prev;//最短路径前一个节点
6 public int scratch;//算法需要
7 public boolean visited;
8 public Vertex(String nm){
9 name=nm;
10 adj=new ArrayList<Edge>();
11 reset();
12 }
13 public void reset(){
14 visited=false;
15 dist=Graph.INFINITY;
16 prev=null;
17 scratch=0;
18 }
19 @Override
20 public int compareTo(Vertex o) {
21 double c=o.dist;
22 return dist<c?-1:dist>c?1:0;
23 }
24 }
25


1 /*
2 * 迪杰斯特拉算法.
3 *
4 * 使用优先级队列来存储V-S集合,这样队首的元素就是要选取加入S的元素。
5 *
6 * 每加入S一个元素,就用它去更新可更新的V-S中的元素。
7 *
8 * */
9 public void dijkstraSlow(String startName){
10 PriorityQueue<Vertex> pq=new PriorityQueue<Vertex>();
11 Vertex start=vertexMap.get(startName);
12 if(start==null)
13 throw new IllegalArgumentException("Hey fk,start vertex is illegal!");
14 clearAll();
15 //pq.add(start);
16 start.dist=0;
17 //构造V-S集合,将顶点全部装入优先级队列(V-S集合).
18 //优先级队列的队首元素即离原点最近的,因为Vertex对象的比较器是
19 //根据Vertex对象到原点的距离来比较的。
20 //其实这里可以进一步优化的,由于开始选中加入S的必定是原点,
21 //且只有刚加入S的顶点的邻接顶点才有可能是新的加入S的顶点,
22 //所以开始只要把原点放入vertexMap即可,第一次将原点加入S,
23 //然后更新其邻接点到原点的距离.从中选出最小的来加入S。
24 //这里为了保持算法清晰,没有进行优化.
25 for(Vertex v:vertexMap.values())
26 pq.add(v);
27
28
29 int seenNum=0;
30 while(!pq.isEmpty()&&seenNum<vertexMap.size()){
31
32 Vertex v=pq.remove();//第一次取的是原点,其余情况自然是到原点最近的点.
33
34 if(v.scratch!=0)//如果v已经加入了S,则跳过
35 continue;
36
37 v.scratch=1;//设置标志位,标志已经加入了S集合.
38
39 seenNum++;//已经确定了多少顶点。
40
41 //利用新加入S的点来更新V-S集合:
42
43 //因为要更新V-S集合中点到原点的路径距离,并从中选取距离最小的点,因此从S集合的点的邻接
44 //点中选取,因为如果不与S的点邻接,就暂时无法更新其距离。而没有更新(即减小路径长)的点不可能被选中.
45 for(Edge e:v.adj){
46 Vertex w=e.dest;
47 double v_to_w=e.cost;
48
49 if(w.dist>v.dist+v_to_w){
50 w.dist=v.dist+v_to_w;//更新其D[w]值
51 w.prev=v;
52 //pq.add(w);//加入优先级队列以备选取.
53
54 //利用优先级队列来排序,取出离原点最近的。
55 //队首的元素满足D[w]=Min(D[v]+D[v to w]).
56 //删掉旧值,更新队列,
57 pq.remove(w);
58 pq.add(w);
59 }
60 }
61
62
63 }
64
65 }
66
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值