Dijkstra 算法

dijkstra算法,最简单的实现需要$O(|V|^2)$。用binary heap实现,可以优化到O((|V|+|E|)lg|V|),如果用fibonacci heap的话,可以优化到O(|E|+|V|lg|V|)。如果图是密集图的话,那这个优化效果也不好,接近$O(|V|^2)$。

fibonacci heap不涉及删除元素的操作有O(1)的平摊时间。 Extract-Min和Delete的数目和其它相比,较小时效率更佳。稠密图每次decrease key只要O(1)的平摊时间,和二项堆的O(lg n)相比是巨大的改进。

For any implementation of vertex set Q the running time is in $O(|E| cdot dk_Q + |V| cdot em_Q)$, where $dk_Q$ and $em_Q$ are times needed to perform decrease key and extract minimum operations in set Q, respectively.

The simplest implementation of the Dijkstra's algorithm stores vertices of set Q in an ordinary linked list or array, and extract minimum from Q is simply a linear search through all vertices in Q. In this case, the running time is $O(|E| + |V|^2) = O(|V|^2)$.

For sparse graphs, that is, graphs with far fewer than $O(|V|^2)$ edges, Dijkstra's algorithm can be implemented more efficiently by storing the graph in the form of adjacency lists and using a self-balancing binary search tree, binary heap, pairing heap, or Fibonacci heap as a priority queue to implement extracting minimum efficiently. With a self-balancing binary search tree or binary heap, the algorithm requires $Theta((|E| + |V|) log |V|)$ time (which is dominated by $Theta( | E | log | V | )$, assuming the graph is connected). To avoid $O(|V|)$ look-up in decrease-key step on a vanilla binary heap, it is necessary to maintain a supplementary index mapping each vertex to the heap's index (and keep it up to date as priority queue Q changes), making it take only $O(log|V|)$ time instead. The Fibonacci heap improves this to $O(|E| + |V| log|V|)$.

  1 #include <iostream>
  2 #include <string>
  3 #include <sstream>
  4 #include <fstream>
  5 #include <vector>
  6 #include <stdlib.h>
  7 #include <string.h>
  8 #include <queue>
  9 #include <map>
 10 #define MAX 10000
 11 using namespace std;
 12 
 13 struct Node {
 14     int dist;
 15     int index;
 16     Node() {}
 17     Node(int i, int d) : index(i), dist(d) {}
 18 };
 19 
 20 ostream &operator<<(ostream& os, Node& node) {
 21     os << node.index << "(" << node.dist << ") "; 
 22     return os;
 23 }
 24 
 25 class Dijkstra{
 26     public:
 27         Dijkstra():n(0), capacity(100) {
 28             this->arr = new Node[capacity];
 29         }
 30 
 31         ~Dijkstra() {
 32             delete[] arr;
 33         }
 34 
 35         void insert(Node v) {
 36             if (n >= capacity) {
 37                 Node* tmp = new Node[capacity << 1];
 38                 memcpy(tmp, arr, sizeof(Node) * capacity);
 39                 capacity <<= 1;
 40             }
 41             keyMap[v.index] = n;
 42             arr[n++] = v;
 43             shiftUp(n - 1);
 44         }
 45 
 46         void shiftDown(int st, int ed) {
 47             Node tmp = arr[st];
 48             while (st < ed) {
 49                 int next = -1;
 50                 int left = st * 2 + 1; // left child node 
 51                 if (left < ed) next = left;
 52                 else break;
 53                 int right = st * 2 + 2;
 54                 if (right < ed && arr[right].dist < arr[next].dist) next = right;
 55                 if (arr[next].dist < arr[st].dist) {
 56                     keyMap[arr[next].index] = st;
 57                     arr[st] = arr[next];
 58                     st = next;
 59                 } else break;
 60             }
 61             keyMap[tmp.index] = st;
 62             arr[st] = tmp;
 63         }
 64 
 65         void shiftUp(int ed) {
 66             Node tmp = arr[ed];
 67             while (ed > 0) {
 68                 int parent = (ed - 1) / 2;
 69                 if (arr[ed].dist < arr[parent].dist) {
 70                     keyMap[arr[parent].index] = ed;
 71                     arr[ed] = arr[parent];
 72                     ed = parent;
 73                 } else break;
 74             }
 75             keyMap[tmp.index] = ed;
 76             arr[ed] = tmp;
 77         }
 78 
 79         void update(int pos, int v) {
 80             if (pos >= n) {
 81                 arr[pos].dist = v;
 82             } else if (arr[pos].dist < v) {
 83                 arr[pos].dist = v;
 84                 shiftDown(pos, n);
 85             } else {
 86                 arr[pos].dist = v;
 87                 shiftUp(pos);
 88             }
 89         }
 90 
 91         bool empty() const {
 92             return n <= 0;
 93         }
 94 
 95         Node top() {
 96             if (empty()) return Node();
 97             return arr[0];
 98         }
 99 
100         void pop() {
101             if (empty()) return;
102             keyMap[arr[n - 1].index] = 0;
103             keyMap[arr[0].index] = n - 1;
104             swap(arr[0], arr[n - 1]);
105             shiftDown(0, --n);
106         }
107 
108         void load(string filename) {
109             ifstream in(filename.c_str());
110             string line;
111             getline(in, line);
112             int n = atoi(line.c_str());
113 
114             while (adjList.size() < n && getline(in, line)) {
115                 vector<int> neigs;
116                 stringstream ss(line);
117                 int v = 0;
118                 while (ss >> v) {
119                     neigs.push_back(v - 1);
120                 }
121                 adjList.push_back(neigs);
122             }
123 
124             while (getline(in, line)) {
125                 vector<int> ws;
126                 stringstream ss(line);
127                 int w = 0;
128                 while (ss >> w) {
129                     ws.push_back(w);
130                 }
131                 weights.push_back(ws);
132             }
133 
134             for (int i = 0; i < adjList.size(); ++i) {
135                 for (int j = 0; j < adjList[i].size(); ++j) {
136                     cout << adjList[i][j] << "(" << weights[i][j] << ") ";
137                 }
138                 cout << endl;
139             }
140         }
141 
142         // dijstra using binary heap, O((|V|+|E|)lg|V|)
143         int run() {
144             if (adjList.empty()) return 0;
145             insert(Node(0, 0));
146             for (int i = 1; i < adjList.size(); ++i) {
147                 insert(Node(i, MAX));
148             }
149 
150             while (!empty()) {    
151                 Node nearest = top(); 
152                 pop(); // O(|V|lg|V|)
153 
154                 for (int j = 0; j < adjList[nearest.index].size(); ++j) {
155                     int d = nearest.dist + weights[nearest.index][j];
156                     int neig = adjList[nearest.index][j];
157                     if (d < arr[keyMap[neig]].dist) update(keyMap[neig], d); // O(|E|lg|V|)
158                 }
159             }
160 
161             for (int i = 0; i < adjList.size(); ++i) {
162                 cout << arr[i] << " ";
163             }
164             cout << endl;
165             return 0;
166         }
167 
168     private:
169         Node* arr;
170         int n;
171         int capacity;
172         map<int, int> keyMap; // from graph index to arr index
173         vector<vector<int> > adjList, weights;
174 };
175 
176 int main()
177 {
178     Dijkstra dij;
179     dij.load("input.txt");
180     dij.run();
181     return 0;
182 }

input.txt:

6 
2 3 6
1 3 4
1 2 4 6
2 3 5
4 6
1 3 5
7 9 14
7 10 15
9 10 11 2
15 11 6
6 9
14 2 9

每一行是顶点个数;

前几行是邻接列表;后几行是对应的边的权重;

注意

dijkstra算法是不能处理负值边和负值环的。floyd可以。
有了负值那它的,整个算法所处理的结点的顺序就有问题了。负值会使后面处理的结点到起点的权值小于已经处理了的结点到起点的权值,该算法就不适用了。
比如:
1->2 2
1->3 100
3->2 -99
用dijkstra求1开始的单源最短路的话因该会得到1->2最短路是2的错误结果。

原文地址:https://www.cnblogs.com/linyx/p/3920342.html