Dijkstra 之最小堆實現

    上一篇完成了樸素實現, 此次用堆完成。GOhtml

 

    圖參考上一篇, Dijkstra 之樸素實現, 這裏從簡。node

     

 建堆其實比較簡單, 看一下僞代碼, 可以有效的理解算法的核心。算法

 BUILD-MAX-HEAP(A)vim

1  heap-size[A] ← length[A]
2   for i ← |_length[A]/2_| downto  1
3        do MIN-HEAPIFY(A, i)    

MIN-HEAPIFY的做用是保持堆的性質。api

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)  ide

 

 代碼實現:性能

  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

 

   原本還想用二項堆和斐波那契堆都實現一下, 看了下有點複雜, 沒有太大必要。 後面再說。ui

   關於上面的實現還想說一點, 註釋也有說明, 在每次提取最小節點和鬆弛操做後, 利用從新建堆的方法從新保持堆的性質, 複雜度O(N)。 還有另一種作法是隻建一次堆, 而後在提取最小節點後,立刻作調整, 複雜度O(logN). 並在每次鬆弛操做後, 因爲節點的d[v]減少, 須要沿着樹枝向上作更新操做, 以保持堆的性質。平均複雜度應該是小於O(logN)的, 這種方法的優勢就是性能稍好, 不過就是有點麻煩。spa

相關文章
相關標籤/搜索