Sir zach

专注于算法,AI, Android以及音视频领域 欢迎关注我的最新博客: zachliu.cn

导航

Dijkstra 之最小堆实现

Posted on 2014-05-17 21:25  SirZach  阅读(1029)  评论(0编辑  收藏  举报

    上一篇完成了朴素实现, 这次用堆完成。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)    

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]

 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, 0110);
248     insertArc(Gp, 035);
249     insertArc(Gp, 121);
250     insertArc(Gp, 132);
251     insertArc(Gp, 244);
252     insertArc(Gp, 313);
253     insertArc(Gp, 329);
254     insertArc(Gp, 342);
255     insertArc(Gp, 426);
256     insertArc(Gp, 407);
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 }
View Code

 

   本来还想用二项堆和斐波那契堆都实现一下, 看了下有点复杂, 没有太大必要。 后面再说。

   关于上面的实现还想说一点, 注释也有说明, 在每次提取最小节点和松弛操作后, 利用重新建堆的方法重新保持堆的性质, 复杂度O(N)。 还有另外一种做法是只建一次堆, 然后在提取最小节点后,马上做调整, 复杂度O(logN). 并在每次松弛操作后, 由于节点的d[v]减小, 需要沿着树枝向上做更新操作, 以保持堆的性质。平均复杂度应该是小于O(logN)的, 这种方法的优点就是性能稍好, 不过就是有点麻烦。