上一篇完成了朴素实现, 这次用堆完成。GO
图参考上一篇, Dijkstra 之朴素实现, 这里从简。
建堆其实比较简单, 看一下伪代码, 能够有效的理解算法的核心。
BUILD-MAX-HEAP(A)
1 heap-size[A] ← length[A]
2 for i ← |_length[A]/2_| downto 1
3 do MIN-HEAPIFY(A, i)
2 for i ← |_length[A]/2_| downto 1
3 do MIN-HEAPIFY(A, i)
MIN-HEAPIFY的作用是保持堆的性质。
MIN-HEAPIFY(A, i)
1 l ← LEFT(i)
2 r ← RIGHT(i)
3 if l ≤ heap-size[A] and A[l] > A[i]
4 then smallest ← l
5 else smallest ← i
6 if r ≤ heap-size[A] and A[r] > A[smallest]
7 then smallest ← r
8 if smallest ≠ i
9 then exchange A[i] <-> A[smallest]
1 l ← LEFT(i)
2 r ← RIGHT(i)
3 if l ≤ heap-size[A] and A[l] > A[i]
4 then smallest ← l
5 else smallest ← i
6 if r ≤ heap-size[A] and A[r] > A[smallest]
7 then smallest ← r
8 if smallest ≠ i
9 then exchange A[i] <-> A[smallest]
10 MAX-HEAPIFY(A, smallest)
代码实现:
1
//
implement of Binary heap
2 #include <stdio.h>
3 #include <malloc.h>
4
5 #define MAX_VERTEX_NUM 20
6 #define INFINITY 65535
7
8 typedef int edgeType;
9
10 typedef struct ArcNode
11 {
12 int adjIndex; // 顶点下标
13 ArcNode *nextArc;
14 edgeType weight;
15 }ArcNode;
16
17 typedef struct VNode
18 {
19 ArcNode* firstArc;
20 }VNode, AdjList[MAX_VERTEX_NUM];
21
22 typedef struct
23 {
24 AdjList adjList;
25 int vexNum;
26 int edgeNum;
27 }ALGraph;
28
29
30 void initALGraph(ALGraph* Gp, int cnt)
31 {
32 Gp->edgeNum = 0;
33 Gp->vexNum = cnt;
34
35 for ( int i = 0; i < cnt; i++)
36 {
37 Gp->adjList[i].firstArc = NULL;
38 }
39 }
40
41 void insertArc(ALGraph* Gp, int head, int tail, int w)
42 {
43 ArcNode* arcNodePt = (ArcNode*)malloc( sizeof( struct ArcNode));
44 if (arcNodePt == NULL)
45 {
46 return;
47 }
48
49 arcNodePt->nextArc = NULL;
50 arcNodePt->adjIndex = tail;
51 arcNodePt->weight = w;
52
53 ArcNode* tailPt = Gp->adjList[head].firstArc;
54 if (tailPt == NULL)
55 {
56 Gp->adjList[head].firstArc = arcNodePt;
57 }
58 else
59 {
60 while (tailPt->nextArc != NULL)
61 {
62 tailPt = tailPt->nextArc;
63 }
64 tailPt->nextArc = arcNodePt;
65 }
66 Gp->edgeNum++;
67 }
68
69
70 void displayGraph(ALGraph G)
71 {
72 ArcNode* arcNodePt;
73
74 for ( int i = 0; i < G.vexNum; i++)
75 {
76 arcNodePt = G.adjList[i].firstArc;
77 printf( " vertex(%d): ", i);
78
79 while (arcNodePt != NULL)
80 {
81 printf( " ->%d(weight: %d) ", arcNodePt->adjIndex, arcNodePt->weight);
82 arcNodePt = arcNodePt->nextArc;
83 }
84 printf( " \n ");
85 }
86 }
87
88 void printRoute( int start, int node, int* pi)
89 {
90 printf( " %d ", node);
91 do
92 {
93 printf( " <-%d ", pi[node]);
94 node = pi[node];
95 } while (node != start);
96
97 }
98
99 int getEdgeWeight(ALGraph G, int head, int tail)
100 {
101 ArcNode* arcNodePt = G.adjList[head].firstArc;
102
103 while (arcNodePt != NULL)
104 {
105 if (arcNodePt->adjIndex == tail)
106 {
107 return arcNodePt->weight;
108 }
109 arcNodePt = arcNodePt->nextArc;
110 }
111
112 return INFINITY;
113 }
114
115
116 void minHeapify( int* Q, int* d, int i)
117 {
118 int l = 2 * i;
119 int r = 2 * i + 1;
120 int smallest = i;
121
122 if (l <= Q[ 0] && d[Q[l]] < d[Q[i]])
123 {
124 smallest = l;
125 }
126
127
128 if (r <= Q[ 0] && d[Q[r]] < d[Q[smallest]])
129 {
130 smallest = r;
131 }
132
133 printf( " smallest = %d\n ", smallest, i, l ,r);
134
135 if (smallest != i)
136 {
137 int temp = Q[i];
138 Q[i] = Q[smallest];
139 Q[smallest] = temp;
140
141 minHeapify(Q, d, smallest);
142 }
143 }
144
145 int extractMin( int* Q, int* d)
146 {
147 int min;
148 min = Q[ 1];
149 Q[ 1] = Q[Q[ 0]];
150 Q[ 0] = Q[ 0] - 1;
151
152 return min;
153 }
154
155 void buildMinHeap( int* Q, int* d, int start)
156 {
157 for ( int i = Q[ 0]/ 2; i >= 1; i--)
158 {
159 minHeapify(Q, d, i);
160 }
161 }
162
163 void initSingleSource(ALGraph G, int start, int* di, int* pi)
164 {
165 for ( int i = 0; i < G.vexNum; i++)
166 {
167 di[i] = INFINITY;
168 pi[i] = 0;
169 }
170
171 ArcNode* arcNodePt = G.adjList[start].firstArc;
172 while (arcNodePt != NULL)
173 {
174 di[arcNodePt->adjIndex] = arcNodePt->weight;
175 arcNodePt = arcNodePt->nextArc;
176 }
177
178 di[start] = 0;
179 }
180
181
182 void dijkstra(ALGraph G, int start, int* d, int* pi, int* Q)
183 {
184 int u, v;
185 int visit[MAX_VERTEX_NUM] = { 0};
186
187 initSingleSource(G, start, d, pi);
188
189 visit[start] = 1;
190
191 Q[ 0] = G.vexNum;
192 for ( int i = 1; i <= Q[ 0]; i++)
193 {
194 Q[i] = i - 1;
195 }
196
197
198 while (Q[ 0] != 0)
199 {
200 buildMinHeap(Q, d, start);
201
202 // 在提取最小点后, 利用每次重新建堆来保持堆的性质。 正常做法是将最后一个结点放到空结点,做下潜操作,复杂度O(logN)。
203 // 建堆复杂度O(N), 效率稍低一点。
204 u = extractMin(Q, d);
205
206 if (u == start)
207 {
208 continue;
209 }
210
211 printf( " extract min node: %d\n ", u);
212
213 visit[u] = 1;
214 ArcNode* arcNodePt = G.adjList[u].firstArc;
215 while (arcNodePt != NULL)
216 {
217 v = arcNodePt->adjIndex;
218 printf( " adjacent to : %d, d[v]=%d, d[u]=%d, w(u,v)=%d\n ", v, d[v], d[u], getEdgeWeight(G, u, v));
219
220 // 松弛操作
221 if (!visit[v] && (d[v] > d[u] + getEdgeWeight(G, u, v)))
222 {
223 d[v] = d[u] + getEdgeWeight(G, u, v);
224 printf( " update d[%d]=%d\n ", v, d[v]);
225 pi[v] = u;
226 printf( " pi[%d]=%d\n\n ", v, pi[v]);
227 }
228 arcNodePt = arcNodePt->nextArc;
229 }
230
231 }
232
233
234
235
236
237 }
238
239
240 int main( int argc, char const *argv[])
241 {
242 #define startpoint 0
243 ALGraph G;
244 ALGraph* Gp = &G;
245
246 initALGraph(Gp, 5);
247 insertArc(Gp, 0, 1, 10);
248 insertArc(Gp, 0, 3, 5);
249 insertArc(Gp, 1, 2, 1);
250 insertArc(Gp, 1, 3, 2);
251 insertArc(Gp, 2, 4, 4);
252 insertArc(Gp, 3, 1, 3);
253 insertArc(Gp, 3, 2, 9);
254 insertArc(Gp, 3, 4, 2);
255 insertArc(Gp, 4, 2, 6);
256 insertArc(Gp, 4, 0, 7);
257
258 printf( " print the graph: \n ");
259 displayGraph(G);
260
261 int d[MAX_VERTEX_NUM];
262 int pi[MAX_VERTEX_NUM];
263 int Q[MAX_VERTEX_NUM + 1]; // Q[0]保存个数
264
265
266 dijkstra(G, startpoint, d, pi, Q);
267
268 for ( int i = 0; i < G.vexNum; i++)
269 {
270 printf( " From %d to %d length is %d. \n ", startpoint, i, d[i]);
271 printf( " Path : ");
272
273 printRoute(startpoint, i, pi);
274 printf( " \n ");
275 }
276
277 return 0;
278 }
2 #include <stdio.h>
3 #include <malloc.h>
4
5 #define MAX_VERTEX_NUM 20
6 #define INFINITY 65535
7
8 typedef int edgeType;
9
10 typedef struct ArcNode
11 {
12 int adjIndex; // 顶点下标
13 ArcNode *nextArc;
14 edgeType weight;
15 }ArcNode;
16
17 typedef struct VNode
18 {
19 ArcNode* firstArc;
20 }VNode, AdjList[MAX_VERTEX_NUM];
21
22 typedef struct
23 {
24 AdjList adjList;
25 int vexNum;
26 int edgeNum;
27 }ALGraph;
28
29
30 void initALGraph(ALGraph* Gp, int cnt)
31 {
32 Gp->edgeNum = 0;
33 Gp->vexNum = cnt;
34
35 for ( int i = 0; i < cnt; i++)
36 {
37 Gp->adjList[i].firstArc = NULL;
38 }
39 }
40
41 void insertArc(ALGraph* Gp, int head, int tail, int w)
42 {
43 ArcNode* arcNodePt = (ArcNode*)malloc( sizeof( struct ArcNode));
44 if (arcNodePt == NULL)
45 {
46 return;
47 }
48
49 arcNodePt->nextArc = NULL;
50 arcNodePt->adjIndex = tail;
51 arcNodePt->weight = w;
52
53 ArcNode* tailPt = Gp->adjList[head].firstArc;
54 if (tailPt == NULL)
55 {
56 Gp->adjList[head].firstArc = arcNodePt;
57 }
58 else
59 {
60 while (tailPt->nextArc != NULL)
61 {
62 tailPt = tailPt->nextArc;
63 }
64 tailPt->nextArc = arcNodePt;
65 }
66 Gp->edgeNum++;
67 }
68
69
70 void displayGraph(ALGraph G)
71 {
72 ArcNode* arcNodePt;
73
74 for ( int i = 0; i < G.vexNum; i++)
75 {
76 arcNodePt = G.adjList[i].firstArc;
77 printf( " vertex(%d): ", i);
78
79 while (arcNodePt != NULL)
80 {
81 printf( " ->%d(weight: %d) ", arcNodePt->adjIndex, arcNodePt->weight);
82 arcNodePt = arcNodePt->nextArc;
83 }
84 printf( " \n ");
85 }
86 }
87
88 void printRoute( int start, int node, int* pi)
89 {
90 printf( " %d ", node);
91 do
92 {
93 printf( " <-%d ", pi[node]);
94 node = pi[node];
95 } while (node != start);
96
97 }
98
99 int getEdgeWeight(ALGraph G, int head, int tail)
100 {
101 ArcNode* arcNodePt = G.adjList[head].firstArc;
102
103 while (arcNodePt != NULL)
104 {
105 if (arcNodePt->adjIndex == tail)
106 {
107 return arcNodePt->weight;
108 }
109 arcNodePt = arcNodePt->nextArc;
110 }
111
112 return INFINITY;
113 }
114
115
116 void minHeapify( int* Q, int* d, int i)
117 {
118 int l = 2 * i;
119 int r = 2 * i + 1;
120 int smallest = i;
121
122 if (l <= Q[ 0] && d[Q[l]] < d[Q[i]])
123 {
124 smallest = l;
125 }
126
127
128 if (r <= Q[ 0] && d[Q[r]] < d[Q[smallest]])
129 {
130 smallest = r;
131 }
132
133 printf( " smallest = %d\n ", smallest, i, l ,r);
134
135 if (smallest != i)
136 {
137 int temp = Q[i];
138 Q[i] = Q[smallest];
139 Q[smallest] = temp;
140
141 minHeapify(Q, d, smallest);
142 }
143 }
144
145 int extractMin( int* Q, int* d)
146 {
147 int min;
148 min = Q[ 1];
149 Q[ 1] = Q[Q[ 0]];
150 Q[ 0] = Q[ 0] - 1;
151
152 return min;
153 }
154
155 void buildMinHeap( int* Q, int* d, int start)
156 {
157 for ( int i = Q[ 0]/ 2; i >= 1; i--)
158 {
159 minHeapify(Q, d, i);
160 }
161 }
162
163 void initSingleSource(ALGraph G, int start, int* di, int* pi)
164 {
165 for ( int i = 0; i < G.vexNum; i++)
166 {
167 di[i] = INFINITY;
168 pi[i] = 0;
169 }
170
171 ArcNode* arcNodePt = G.adjList[start].firstArc;
172 while (arcNodePt != NULL)
173 {
174 di[arcNodePt->adjIndex] = arcNodePt->weight;
175 arcNodePt = arcNodePt->nextArc;
176 }
177
178 di[start] = 0;
179 }
180
181
182 void dijkstra(ALGraph G, int start, int* d, int* pi, int* Q)
183 {
184 int u, v;
185 int visit[MAX_VERTEX_NUM] = { 0};
186
187 initSingleSource(G, start, d, pi);
188
189 visit[start] = 1;
190
191 Q[ 0] = G.vexNum;
192 for ( int i = 1; i <= Q[ 0]; i++)
193 {
194 Q[i] = i - 1;
195 }
196
197
198 while (Q[ 0] != 0)
199 {
200 buildMinHeap(Q, d, start);
201
202 // 在提取最小点后, 利用每次重新建堆来保持堆的性质。 正常做法是将最后一个结点放到空结点,做下潜操作,复杂度O(logN)。
203 // 建堆复杂度O(N), 效率稍低一点。
204 u = extractMin(Q, d);
205
206 if (u == start)
207 {
208 continue;
209 }
210
211 printf( " extract min node: %d\n ", u);
212
213 visit[u] = 1;
214 ArcNode* arcNodePt = G.adjList[u].firstArc;
215 while (arcNodePt != NULL)
216 {
217 v = arcNodePt->adjIndex;
218 printf( " adjacent to : %d, d[v]=%d, d[u]=%d, w(u,v)=%d\n ", v, d[v], d[u], getEdgeWeight(G, u, v));
219
220 // 松弛操作
221 if (!visit[v] && (d[v] > d[u] + getEdgeWeight(G, u, v)))
222 {
223 d[v] = d[u] + getEdgeWeight(G, u, v);
224 printf( " update d[%d]=%d\n ", v, d[v]);
225 pi[v] = u;
226 printf( " pi[%d]=%d\n\n ", v, pi[v]);
227 }
228 arcNodePt = arcNodePt->nextArc;
229 }
230
231 }
232
233
234
235
236
237 }
238
239
240 int main( int argc, char const *argv[])
241 {
242 #define startpoint 0
243 ALGraph G;
244 ALGraph* Gp = &G;
245
246 initALGraph(Gp, 5);
247 insertArc(Gp, 0, 1, 10);
248 insertArc(Gp, 0, 3, 5);
249 insertArc(Gp, 1, 2, 1);
250 insertArc(Gp, 1, 3, 2);
251 insertArc(Gp, 2, 4, 4);
252 insertArc(Gp, 3, 1, 3);
253 insertArc(Gp, 3, 2, 9);
254 insertArc(Gp, 3, 4, 2);
255 insertArc(Gp, 4, 2, 6);
256 insertArc(Gp, 4, 0, 7);
257
258 printf( " print the graph: \n ");
259 displayGraph(G);
260
261 int d[MAX_VERTEX_NUM];
262 int pi[MAX_VERTEX_NUM];
263 int Q[MAX_VERTEX_NUM + 1]; // Q[0]保存个数
264
265
266 dijkstra(G, startpoint, d, pi, Q);
267
268 for ( int i = 0; i < G.vexNum; i++)
269 {
270 printf( " From %d to %d length is %d. \n ", startpoint, i, d[i]);
271 printf( " Path : ");
272
273 printRoute(startpoint, i, pi);
274 printf( " \n ");
275 }
276
277 return 0;
278 }
本来还想用二项堆和斐波那契堆都实现一下, 看了下有点复杂, 没有太大必要。 后面再说。
关于上面的实现还想说一点, 注释也有说明, 在每次提取最小节点和松弛操作后, 利用重新建堆的方法重新保持堆的性质, 复杂度O(N)。 还有另外一种做法是只建一次堆, 然后在提取最小节点后,马上做调整, 复杂度O(logN). 并在每次松弛操作后, 由于节点的d[v]减小, 需要沿着树枝向上做更新操作, 以保持堆的性质。平均复杂度应该是小于O(logN)的, 这种方法的优点就是性能稍好, 不过就是有点麻烦。