最短路问题(有边权问题,无负权边,无权图就BFS即可 01边权使用双端队列BFS)
难点:抽象建图,如何把题意变为点和边。
- 无向图也是一种特殊的有向图,算法不变。
- 如果存在重边,必须保留最短的那条重边。
- 建图有两种方式,之前的文章已经有提过 (建图不用new,耗费时间长)
- 邻接表 :适合稀疏图 和 树 ( O mlogn)
- 邻接矩阵 :适合稠密图 (O n^2)
- 选择哪种方式则根据题目 给定数据范围推出。
- 由于dijk算法是基于贪心的算法,因此禁止负权边的出现。
- 稠密图 On^2: (n超过10万就超时)
-
问题定义: 求从1号点到其他所有点的最短距离
-
算法思路:(确定->更新)
- 初始化距离dist[1] = 0; else dist[i] = 0x3f3f3f3f;
- 集合dist: 当前已经确定的最短的点 (一旦确定,这个点的距离就不会再变)
- for (i 1~n) On
- for(j 1~n) t = 找到一个不在dist中,且距离1号点距离最近的点。On (prim算法这里更新的是到集合的最短距离)
- for(j 1~n) 用此点,来更新其他所有点到达1号点的距离 On
- 每次迭代都可以确定一个点,确定n此即确定所有点的最短距离
-
代码:
Map[x][y] = Math.min(Map[x][y],z); //这里一定要保存重边最小 //---------------------------------------------- static int dijkstra(){ Arrays.fill(dist,0x3f3f3f); //初始化为到不到的距离。 dist[1] = 0; for(int i =0;i<n;i++){ //On 需要确定n个点。循环n次 int t = -1; //这个用于每次都能选择一个没有确定的点。 for(int j = 1;j<=n;j++){ //在没确定的点中,找到一个离起点最近的点。 if(st[j]==false && (t==-1 || dist[t] > dist[j])) t = j; //j还没有确定最短路。 } st[t] = true;//然后这个点确定。dist[t]已经确定 for(int j =1;j<=n;j++){ //借用此点更新其他点到1号点的距离 On //On dist[j] = Math.min(dist[j], dist[t] + Map[t][j]); } } if(dist[n]==0x3f3f3f) return -1; //1 和 n 不连通。 return dist[n]; }
-
- 稀疏图 Omlogn (点多边少邻接表)
- 问题定义同上
- 算法思路同上:唯一优化的地方 就是确定离1号点最近的点使用堆来保存。Ologn
- 可以使用手写堆。也可以使用优先队列。(优先队列不能修改任意值,所以会存在很多冗余,修改一个就会添加一个,但是不影响使用)
- 代码
static int dijkstra(){ //必背 Arrays.fill(dist,0x3f3f3f3f); dist[1] = 0; PriorityQueue<Pair2> heap = new PriorityQueue<>(); heap.add(new Pair2(0,1)); //1号点最短距离为0 pair(dist,num) while (!heap.isEmpty()){ //堆不空 Pair2 pair = heap.poll(); //直接拿到堆中 最近的那个点。 int t = pair.num; //提出此点 int distance = pair.dist; if(st[t]==true) continue; //已经确定了。就算堆里面还剩有也不会用了。 这里是核心。防止死循环。 st[t] = true;//确定这个点 for(int i = h[ver];i!=-1;i=NE[i]){ //遍历所有临边包括重边 Om int j = E[i]; if(dist[j] > distance+w[i]){ // 如果到1号点距离变短了 dist[j] = distance + w[i]; heap.add(new Pair2(dist[j],j));//加就完事,无所谓上面有判断 } } } if(dist[n]==0x3f3f3f3f) return -1; //1 和 n 不连通。 return dist[n]; } class Pair2{ int num; //点的id; int dist; //距离1号点的距离 } ```