r_heap.w
上传用户:gzelex
上传日期:2007-01-07
资源大小:707k
文件大小:36k
开发平台:

MultiPlatform

  1. documentstyle[11pt,comments,fullpage,pstricks,pst-node,pst-coil]{cweb}
  2. advancetextheight by 1.8cm
  3. advanceoddsidemargin by -2mm
  4. %documentstyle[comments,a4,mpicover,titlepage]{cweb}
  5. %pagestyle{headings}
  6. title{Radix heaps, an efficient implementation for priority queuesthanks{This
  7. work was partially supported by the ESPRIT Basic Research Actions Program, 
  8. under contract No. 7141 (project ALCOM~II).}}  
  9.       
  10. author{Jochen K"onemannfootnotemark[2]
  11. and Christoph Schmitzthanks{Max-Planck-Institut f"ur Informatik,
  12. Im Stadtwald, D-66123 Saarbr"ucken and Computer Science Department,
  13. Universit"at des Saarlandes, D-66041 Saarbr"ucken}
  14. and Christian Schwarzthanks{Max-Planck-Institut f"ur Informatik,
  15. Im Stadtwald, D-66123 Saarbr"ucken}}
  16. %repnumber{95-1-009}
  17. %begin{keywords}
  18. %R_HEAP,
  19. %DIJKSTRA,
  20. %LEDA
  21. %end{keywords}
  22. begin{document}
  23. maketitle
  24. begin{abstract}
  25. We describe the implementation of a data structure called radix heap,
  26. which is a priority queue with restricted functionality. 
  27. Its restrictions are observed by Dijkstra's algorithm, which uses
  28. priority queues to solve the single source shortest path problem
  29. in graphs with nonnegative edge costs.
  30. For a graph with $n$ nodes and $m$ edges and real-valued edge costs,
  31. the best known theoretical bound for the algorithm is $O(m+nlog n)$.
  32. This bound is attained by using Fibonacci heaps to implement
  33. priority queues. 
  34. If the edge costs are integers in the range $[0ldots C]$, then using
  35. our implementation of radix heaps for Dijkstra's algorithm 
  36. leads to a running time of $O(m+nlog C)$.
  37. We compare our implementation of radix heaps with an existing implementation
  38. of Fibonacci heaps in the framework of Dijkstra's algorithm. Our 
  39. experiments exhibit a tangible advantage for radix heaps over Fibonacci heaps
  40. and confirm the positive influence of small edge costs on the running time.
  41. end{abstract}
  42. tableofcontents
  43. @i ../leda_types.w
  44. @* Introduction. 
  45. In this paper, we describe the implementation of a data structure named
  46. {bf radix heap}. For convenience, we will also refer to this structure
  47. as |r_heap| in the following.
  48. A radix heap can be used to implement the data type {em priority queue}.
  49. A priority queue stores a collection of items, each of which 
  50. has a {em key}. The keys in the queue come from a linearly ordered universe.
  51. There are operations available to update a priority queue. 
  52. Operation |insert| adds a new item with a given key to the queue.
  53. Operation |decrease_key| takes a given item and decreases its key
  54. to a given value if applicable, i.e. if this value is smaller than
  55. the element's current key.
  56. Furthermore, with operation |del_item| we can delete any given item 
  57. from the queue.
  58. The main query operation on a priority queue is |find_min|, which returns
  59. an item containing a key that is minimal among all keys occurring in the queue.
  60. Searching for any other particular key is not supported, and this is
  61. what sets priority queues apart from e.g. sorted sequences or dictionaries.
  62. For convenience, a combination of the query |find_min| and the update
  63. |del_item| is regularly offered on priority queues as |del_min|.
  64. Priority queues find an important application in the {em shortest path
  65. problem:} given a graph $G$ with $n$ nodes and $m$ edges where each edge
  66. is labeled with a nonnegative real-valued cost, the problem is
  67. to compute the shortest distance from a designated node $s$ to all other
  68. nodes in $G$. A solution to this problem is provided by {em Dijkstra's
  69. algorithm/} cite{dijkstra}. Its running time is dominated by 
  70. $n$ |insert|, |find_min| and |del_item| operations, plus $m$
  71. |decrease_key| operations carried out on a priority queue of size at most $n$.
  72. The theoretically most efficient algorithm is obtained by using 
  73. {em Fibonacci heaps/} cite{fheap} to implement the priority queue.
  74. A Fibonacci heap provides |decrease_key| for $O(1)$ amortized time
  75. and the other mentioned operations for $O(log n)$ amortized time.
  76. This leads to a running time of $O(m+nlog n)$ for Dijkstra's algorithm.
  77. While this is optimal for real-valued edge costs, 
  78. improvements are possible if edge costs are bounded integers. 
  79. This was investigated by Ahuja et. al. cite{rheap}.
  80. Their paper contains two solutions and for both, the running time depends 
  81. on the bound $C$ given for the edge costs.
  82. While a one-level radix heap leads to a running time of
  83. $O(m+nlog C)$, a complicated two-level radix heap improves this bound
  84. to $O(m+nsqrt{log C})$.
  85. We chose to implement the one-level heap due to its simplicity in
  86. order to include it into the data structures library LEDA cite{ledaMN,lman,ledabook}.
  87. Radix heaps exploit some special properties of Dijkstra's algorithm
  88. in the use of priority queues. 
  89. For every node $v$ of $G$, the algorithm maintains a tentative
  90. distance $d(v)$ that is not smaller than its actual distance 
  91. to $s$, say $widehat{d}(v)$.
  92. Each node $vneq s$ has $d(v)=infty$ at the start of the algorithm
  93. and enters the priority queue during the algorithm with a finite $d(v)$.
  94. It is stored there using $d(v)$ as its key until its distance to $s$ is known, 
  95. i.e. $d(v)=widehat{d}(v)$.
  96. Assume that all edge costs are in the integer range $[0ldots C]$.
  97. Then the following properties hold:
  98. begin{enumerate}
  99. item[(1)] For any node $v$: if $d(v) < infty$, then $0le d(v)le nC$ 
  100. item[(2)] Let $x$ be the node with minimal $d(x)$ in the priority queue. 
  101. Then $d(x)=widehat{d}(x)$.
  102. Furthermore, let $v$ be any other node in the graph whose final distance
  103. $widehat{d}(v)$ is not yet known. Then $widehat{d}(v)ge d(x)$, and if $v$ is
  104. stored in the queued, we have $d(v)in [d(x)ldots d(x)+C]$.
  105. end{enumerate}
  106. %
  107. Property (2) particularly means that the sequence of minimum keys
  108. is nondecreasing. A priority queue with the above properties
  109. is sometimes called a {em bounded monotonic heap}.
  110. These restrictions have the following effect on the specification
  111. of the operations on |r_heap|s. Both |insert| and |decrease_key| are
  112. given a key value as one of their arguments. It is required that this
  113. key is not smaller than the current minimum key in the heap. 
  114. During the implementation, we will mention some parts of the analysis 
  115. which are essential for the understanding of the implementation. 
  116. The reader is referred to the original paper cite{rheap} for more details.
  117. We implement the data structure |r_heap| in C++ with the intention to 
  118. include it into the LEDA library.
  119. To achieve this goal, we follow LEDA conventions in the specification 
  120. as well as in the implementation.
  121. The paper is organized as follows. First, 
  122. we give the specification
  123. of the data type in the header file {tt r_heap.h}.
  124. Then, we describe the implementation of the functions that were
  125. declared in the header file.
  126. Following this, we describe a LEDA implementation of Dijkstra's
  127. algorithm using generic priority queues.
  128. Using this algorithm, we compare the performance of radix heaps
  129. with Fibonacci heaps, the default implementation of priority queues 
  130. provided by LEDA.
  131. We conclude the paper with experimental results.
  132. @* The header file.
  133. The header defines two classes, one to represent a single heap element,
  134. and another to describe the structure of the heap itself, along with
  135. the operations defined on the heap. In the description of both classes,
  136. we use |GenPtr|, the generic pointer type of LEDA. Substituting
  137. actual types for an application will be done by LEDA's priority queue
  138. interface, with which we integrate our implementation. See
  139. cite{ledabook} for more details on this issue.
  140. Let us begin with some terminology. 
  141. In a radix heap, elements are grouped into |buckets| according to their key.
  142. The first bucket will always contain the elements with the current
  143. minimum key (and only those), which enables us to carry out |find_min|
  144. efficiently. 
  145. The maximal difference between any two keys in a radix heap is a certain
  146. integer $C$ which is given when the heap is created.
  147. There are $B=lceillog(C+1)rceil+2$ buckets. For each bucket $i,0le i < B$, 
  148. there is a number $u[i]$ which gives an upper bound on the keys stored 
  149. in that bucket.
  150. More specifically, let $min$ be the minimum key stored in the heap, and 
  151. define $u[-1]=min-1$. Then, as mentioned above, $u[0]=min$, and
  152. begin{enumerate}
  153. item $u[-1] < u[0] le u[1]leldotsle u[B-1]$,
  154. item an element with key $k$ belongs to bucket $i$, where $0le i le B-1$,
  155. if $u[i-1] < k le u[i]$,
  156. item $u[i]le u[i-1]+2^{i-1}$ for $1le i < B-1$,
  157. item $u[B-1]=mbox{MAXINT}$.
  158. end{enumerate}
  159. @ A single heap element is stored in an |r_heap_node|.
  160. First of all, this structure contains a |key| field.
  161. The field |inf| holds associated information.
  162. The |bucket| field gives the number of the bucket which currently
  163. contains the element.
  164. We want to maintain the elements of a bucket in a doubly linked list.
  165. Pointers |succ| and |pred| are provided for this purpose.
  166. For convenience, we also define |r_heap_item| as a pointer to an |r_heap_node|.
  167. Activation of the LEDA memory manager will improve the efficiency
  168. of memory (de-)allocation with |new| and |delete|.
  169. @<r_heap_node@>=
  170. class r_heap_node  
  171. {
  172.   friend class r_heap;
  173.   GenPtr key;                // key
  174.   GenPtr inf;                // information
  175.   int   bucket;             // number of bucket containing the node
  176.   r_heap_node *succ,*pred;   // pointers for list maintenance
  177.  
  178. public:@/
  179.   r_heap_node (GenPtr k,GenPtr i) : key(k),inf(i),bucket(0),succ(nil),
  180.                                     pred(nil)@+ {@+}
  181.   LEDA_MEMORY(r_heap_node)
  182. };  
  183. typedef r_heap_node* r_heap_item;
  184. @ Class |r_heap| defines the radix heap itself.
  185. First, there is a constant $C$ for the maximal key span in the heap.
  186. That is, the keys stored in the heap always come from an integer range
  187. $[minldots min + C]$.
  188. Each bucket maintains a list of |r_heap_node|s contained in it.
  189. Access to the buckets is provided by the array |buckets[]|.
  190. Along with this array, there is another array |u[]| such that for any $i$,
  191. the keys of the elements stored in $buckets[i]$ are bounded by $u[i]$.
  192. Both arrays change dynamically during the lifetime of an |r_heap|.
  193. We will need to adjust the bucket boundaries recorded in |u[]| from time
  194. to time, and to do this more efficiently, we tabulate the 
  195. appropriate key range for the buckets in an array |bsize[]|.
  196. This array remains unchanged during the lifetime of an |r_heap|.
  197. |B| denotes the number of buckets necessary to store the elements
  198. in the heap for a given key span $C$.
  199. The class declaration provides all operations that are contained
  200. in the LEDA priority queue interface, because we want to use |r_heap| in
  201. this framework.
  202. The counter |si| records the number of elements currently stored in the
  203. heap. Its purpose is to make the operations |empty| and |size|
  204. particularly simple and efficient. 
  205. pagebreak[3]
  206. @<class r_heap@>=
  207. class r_heap { @;
  208. /* data kept in an |r_heap| */
  209.   int C;  // maximal difference between two keys in the heap
  210.   r_heap_item *buckets; // buckets of the |r_heap|
  211.   int *u; // upper bounds on the key intervals corresponding to the buckets
  212.   int B; //  number of buckets
  213.   int si; // current number of elements stored in the heap
  214.   int* bsize;  // table used to (re-)initialize the array |u| or part of it
  215.   @;
  216. /* private functions that facilitate the descriptions of the |r_heap| operations */
  217.   inline void set_bucket_bounds(int min, int upto);
  218.   inline int findbucket (r_heap_item,int);
  219.   void copy_heap (const r_heap&);
  220.   inline void add_item (r_heap_item,int);
  221.   inline void rm_item (r_heap_item);
  222. @;
  223. /* non-public functions concerned with the use of |r_heap| within LEDA */
  224.   virtual void print_key(GenPtr)  const@+ {@+}
  225.   virtual void print_inf(GenPtr)  const@+ {@+}
  226.   virtual void clear_key(GenPtr&)  const@+ {@+}
  227.   virtual void clear_inf(GenPtr&)  const@+ {@+}
  228.   virtual void copy_key(GenPtr&)   const@+ {@+}
  229.   virtual void copy_inf(GenPtr&)   const@+ {@+}
  230.   virtual int  int_type() const@+ {@+ return 0;@+ }
  231. protected:@;
  232.   r_heap_node* item(void* p) const@+ {@+ return (r_heap_node*)p;@+ }
  233. public:@;
  234.   r_heap(int C); 
  235.   // the maximal difference between two keys in the heap needs to be provided upon initialization 
  236.   
  237.   r_heap()@+ {@+ error_handler(1,"r_heap: must initialize with int C>=0");@+ }
  238.   r_heap(const r_heap&);
  239.   r_heap& operator=(const r_heap&);
  240.   virtual ~r_heap() @+ {@+ clear();@+ }
  241.   
  242.   r_heap_item find_min() const;
  243.   
  244.   r_heap_item insert(GenPtr k, GenPtr i);@+// precondition: |k >= key(find_min())|
  245.     
  246.   void del_item(r_heap_node *x);
  247.   void del_min();
  248.   void decrease_key(r_heap_node* x,GenPtr k);@+// precond.: |key(find_min())<=k<key(x)|
  249.  
  250.   void change_inf(r_heap_node* x,GenPtr i);
  251.   
  252.   GenPtr  key(r_heap_node *x) const@+ {@+ return x->key ;@+ }
  253.   GenPtr  inf(r_heap_node *x) const@+ {@+ return x->inf;@+ }
  254.   void clear();
  255.   int  size()  const;
  256.   int  empty() const;
  257. @;
  258.   /* functions that are used by the LEDA iteration macros */
  259.   
  260.   r_heap_item first_item() const;
  261.   r_heap_item next_item(r_heap_item p) const;
  262.   void print_contents (ostream& chk = cout) const;
  263. };@;  
  264. /* dummy I/O and cmp functions */
  265. inline void Print(const r_heap&, ostream&)@+ { @+}
  266. inline void Read (r_heap&, istream&)@+ {@+ }
  267. inline int  compare(const r_heap&,const r_heap&)@+ {@+ return 0;@+ }
  268. @ It is necessary to include a header file containing standard
  269. functionality of LEDA.
  270. @<includes@>=
  271. #include <LEDA/basic.h>
  272. @ The header file now looks as follows:
  273. @(r_heap.h@>=
  274. @<includes@>@;
  275. @<r_heap_node@>@;
  276. @<class r_heap@>
  277. @* The implementation.
  278. In the following sections, we present an annotated implementation of the 
  279. member functions of class |r_heap|.
  280. @ We need a constructor that creates an empty |r_heap|.
  281. In the previous section, we have seen that class |r_heap|
  282. does not permit a constructor without arguments. Instead, there is 
  283. a constructor that receives an |int| argument. This argument determines
  284. the constant |C| and is used to calculate |B|, the number of buckets
  285. in the heap.
  286. The constructor also allocates space for the arrays |buckets[]|, |u[]| 
  287. and |bsize[]|.
  288. After this, |buckets[]| and |bsize[]| are initialized.
  289. The array |u[]| is supposed to keep the upper bounds on the keys stored
  290. in the buckets. These values cover the range $[minldots min+C]$ of
  291. keys stored in the heap, where $min$ is the current minimum key.
  292. Since the invocation of this constructor creates an empty heap,
  293. we do not know $min$ and defer the initialization of array |u[]|
  294. until the first |insert| operation.
  295. Nevertheless, we already set the last entry, |u[B-1]|, whose value 
  296. MAXINT remains unchanged throughout the lifetime of the |r_heap|.
  297. @<constructors@>=
  298. r_heap::r_heap (int c)
  299. {
  300.   C = c;
  301.   B = int(ceil(log(C)/log(2))) + 2;
  302.   buckets = (r_heap_item*)new int[B];
  303.   for (int i = 0; i < B; i++)
  304.     buckets[i] = nil;
  305.   bsize = new int[B];
  306.   u = new int[B];
  307.   bsize[0] = 1;
  308.   bsize[B-1] = MAXINT;
  309.   for (i = 1; i < B-1; i++)
  310.     bsize[i] = 1 << (i-1);
  311.   u[B-1] = MAXINT; /* this value won't change throughout the computation
  312.       the other u[] values will be initialized by |insert| */
  313.   si = 0;
  314. }
  315. @ Throughout this section we need functions that add an
  316. |r_heap_item| to---or remove it from---a bucket. Each of the 
  317. following functions operates on a doubly linked list which 
  318. connects the |r_heap_item|s that belong to a bucket.
  319. @<private functions@>=
  320. inline void r_heap::add_item (r_heap_item it,int bnr)
  321. {
  322.   it->succ=buckets[bnr];
  323.   if (buckets[bnr]!=nil)
  324.     buckets[bnr]->pred=it;
  325.   it->pred=nil;
  326.   it->bucket=bnr;
  327.   buckets[bnr]=it;
  328. }
  329. inline void r_heap::rm_item (r_heap_item it)
  330. {
  331.   if (it->pred!=nil)
  332.     (it->pred)->succ=it->succ; else
  333.     buckets[it->bucket]=it->succ;
  334.   if (it->succ!=nil)
  335.     (it->succ)->pred=it->pred;
  336. }
  337. @ For the copy constructor and for the similar assignment operator,
  338. which are described below, we use an auxiliary function.
  339. Called for an |r_heap| object, the function |copy_heap| is given 
  340. another |r_heap| as its argument and replicates that heap 
  341. in its own member variables. 
  342. @<private functions@>+=
  343. void r_heap::copy_heap (const r_heap &rh)
  344. {
  345.   C = rh.C;
  346.   B = rh.B;
  347.   si = rh.si;
  348.   buckets = (r_heap_item*)new int[B];
  349.   u = new int[B];
  350.   bsize = new int[B];
  351.  
  352.   for (int i = 0; i < B; i++) {
  353.     u[i] = rh.u[i];
  354.     bsize[i] = rh.bsize[i];
  355.   }
  356.   r_heap_item item1,item2;
  357.   for (i = 0; i < rh.B; i++) {
  358.     if (rh.buckets[i]!=nil) {
  359.       item1=rh.buckets[i];
  360.       do {
  361.         item2=new r_heap_node(item1->key,item1->inf);
  362.         add_item(item2,i);
  363.         item1=item1->succ;
  364.       } while (item1!=nil);
  365.     } else
  366.     buckets[i]=nil;       
  367.   }
  368. }    
  369. @ Using the function |copy_heap|, the copy constructor and the assignment
  370. operator are simple.
  371. @<constructors@>+=
  372. r_heap::r_heap (const r_heap &rh)
  373. {
  374.   copy_heap(rh);
  375. }
  376. r_heap &r_heap::operator = (const r_heap &rh)
  377. {
  378.   if (this != &rh) {
  379.     delete []buckets;
  380.     delete []u;
  381.     
  382.     copy_heap(rh);
  383.   }
  384. }
  385. @ During the lifetime of an |r_heap|, we often change the bucket boundaries.
  386. The |set_bucket_bounds| function serves this purpose.
  387. Its arguments are a key value |min| and a bucket number |upto|.
  388. The function sets $u[0]=min$ and then redefines $u[1],ldots,u[upto-1]$.
  389. Since it is a private function, we do not check these arguments
  390. for integrity, but the requirements are
  391. $1 < upto < B-1$ and $u[0]le min le u[upto]$.
  392. According to the original paper cite{rheap}, we need compute 
  393.        $u[i] = mbox{Min}(u[i-1] + bsize[i], u[upto])$
  394. for $i = 1,ldots, upto-1$.
  395. However, we know that the sequences $u[]$ and $bsize[]$ are 
  396. monotonically nondecreasing,
  397. so the outcome of the above Min computation is also monotone.
  398. Hence, when computing $u[i]$, we explicitly check whether
  399. $u[i-1] + bsize[i] > u[upto]$.
  400. If this is the case, then $u[j]=u[upto],, ile jle upto -1,$
  401. which simplifies the computation for these $j$.
  402.      
  403. @<private functions@>+=
  404. inline void r_heap::set_bucket_bounds(int min, int upto) {  
  405.   u[0] = min;
  406.   for (int i = 1; i < upto; i++) {
  407.     u[i] = u[i-1] + bsize[i];
  408.     if (u[i] > u[upto])
  409.       break;
  410.   }
  411.   for ( ; i < upto; i++)
  412.     u[i] = u[upto];
  413. }
  414.   
  415. @ We are now ready to describe the implementation of the important
  416. priority queue operations.
  417. Our implementation slightly deviates from the one sketched in cite{rheap}
  418. by additionally maintaining the following invariant for a non-empty |r_heap|:
  419. begin{quote}
  420.    Before and after each operations, the elements with minimum key are 
  421.    contained in the first bucket of the |r_heap|, i.e. in |bucket[0]|.
  422. end{quote}
  423. @ Operation |find_min|, which returns an element with minimum key, has
  424. a simple implementation due to the aforementioned invariant.
  425. @<public functions@>=
  426. r_heap_item r_heap::find_min (void) const  {
  427.   if (si > 0)
  428.     return buckets[0];
  429.   else
  430.     error_handler(1,"r_heap::find_min : Heap is empty!");  
  431. }
  432. @ Operation |insert| adds a new element the heap. To do this,
  433. it is given two arguments which represent a key and an 
  434. information, respectively.
  435. First, an |r_heap_node| is created using the key and the information
  436. argument.
  437. If the heap was previously empty, we set the interval bounds using the 
  438. key argument and put the newly created |r_heap_node| into the first bucket.
  439. Otherwise, the bucket bounds are already initialized, and to find the correct
  440. bucket for the new element, we scan the bucket boundaries for the ``slot''
  441. containing the new key, in descending order starting with the last bucket.
  442. After the new |r_heap_node| is inserted into the data structure in either
  443. way, we conclude the operation by updating the element counter and 
  444. returning a pointer to the new node.
  445. @<public functions@>+=
  446. r_heap_item r_heap::insert (GenPtr k, GenPtr i) {
  447.  
  448.   r_heap_item item=new r_heap_node(k,i);
  449.  
  450.   if (si > 0) { /* We check whether the operation respects the r_heap conditions */
  451.     if (int(k) < u[0] || int(k) > u[0] + C) {
  452.       string s("r_heap::insert: k= %d out of range [%d,%d]n",int(k),u[0],u[0]+C);
  453.       error_handler(1,s);
  454.     }
  455.     int i = findbucket(item,B-1);
  456.     add_item(item, i);
  457.   }
  458.   else {
  459.     set_bucket_bounds((int)k, B-1);
  460.     buckets[0] = item;
  461.     item->bucket = 0;
  462.   }
  463.   si++;
  464.   
  465.   return item;
  466. }           
  467. @ We haven't described |findbucket| yet. This is a private function which,
  468. given an |r_heap_item| as its first argument, finds the appropriate bucket 
  469. for that item. It does this by scanning the buckets in decreasing order 
  470. starting from the bucket whose number is given as the second argument.
  471. Since |find_bucket| is private, we do not check any invariants. This is
  472. up to the public functions calling |find_bucket|.
  473. @<private functions@>+=
  474. inline int r_heap::findbucket (r_heap_item it,int start) {
  475.   if (int(it->key) == u[0])
  476.     start = -1;
  477.   else 
  478.     while (int(it->key) <= u[--start]) ;
  479.   // now $u[start] < int(it->key) le u[start+1]$
  480.     
  481.   return (start + 1);
  482. }
  483. @ Operation |del_min| may be expressed using |find_min| and |del_item|. 
  484. For efficiency reasons, we replace the call of |find_min| by a direct
  485. access to the head of the first bucket, since our invariant guarantees
  486. that |bucket[0]| is not empty if the heap is not empty.
  487. @<public functions@>+=
  488. void r_heap::del_min (void)
  489. {
  490.   if (si>0)  {
  491.     r_heap_item it=buckets[0];
  492.     del_item(it);
  493.   } 
  494.   else
  495.     error_handler(1,"r_heap::del_min : Heap is empty!");
  496. }
  497. @ Operation |decrease_key| is given an item $x$ sets its key to a given
  498. value $k$. According to the specification given before, the
  499. new value must be smaller than the previous key of $x$ but not smaller
  500. than the current minimum key of the heap.
  501. If these requirements are satisfied, the key of the item is decreased 
  502. as desired. Additionally, if the new key violates the boundaries of the
  503. item's bucket, the item is moved to a bucket with lower index.
  504. @<public...@>+=
  505. void r_heap::decrease_key (r_heap_node *x,GenPtr k) {
  506.   if ( (int(k) < int(x->key)) && (int(k) >= u[0]) )  {
  507.     x->key=k;
  508.     if ( (int(k) <= u[x->bucket-1]) ) {
  509.       rm_item(x);
  510.       int i = findbucket(x,x->bucket);
  511.       add_item(x,i);
  512.     }
  513.   }
  514.   else {
  515.     string s("r_heap::decrease_key: k= %d out of range [%d,%d]n",int(k),u[0],int(x->key)-1);
  516.     error_handler(1,s);
  517.   }  
  518. }
  519. @ The following operation allows the user to change the associated 
  520. information of an item. It has no influence on the heap structure,
  521. however, since we can directly access the information by the given
  522. |r_heap_item|.
  523. @<public...@>+=
  524. void r_heap::change_inf (r_heap_node *x,GenPtr i)
  525. {
  526.   x->inf=i;
  527. }
  528. @ Operation |clear| deletes all elements of the |r_heap| and deallocates the
  529. storage space taken by the elements.
  530. @<public...@>+=
  531. void r_heap::clear (void)
  532. {
  533.   r_heap_item it;
  534.   for(int i=1;i<B;i++)
  535.   while ((it=buckets[i])!=nil)
  536.   {
  537.     rm_item(it);
  538.     delete it;
  539.   }
  540. }
  541. @ Operation |del_item| removes a given item from the heap.
  542. This is the most complicated of our operations.
  543. If the heap is not empty after the given item has been removed, but the 
  544. first bucket does no longer contain any element, then the invariant
  545. is violated.
  546. To reinstate the invariant, we first find the element that holds
  547. the minimum key. We place this element in |bucket[0]| and readjust
  548. the bucket boundaries accordingly. Finally, all the elements which
  549. previously were in the same bucket as the new heap minimum are
  550. redistributed to buckets with lower indices according to the new boundaries.
  551. @<public...@>+=
  552. void r_heap::del_item (r_heap_node *x)
  553. {
  554.   int buck = x->bucket;
  555.   rm_item(x);
  556.   delete x;
  557.  
  558.   if ( (si > 1) && (buck == 0) && (buckets[0] == nil) )
  559.   {
  560.     r_heap_item item;
  561.     @<find new minimum@>
  562.     @<reorganize r_heap@>        
  563.   }
  564.   si--;
  565. }
  566. @ We now describe how to find the element that holds the new minimum key.
  567. Knowing that |bucket[0]| is empty, we check the buckets in ascending
  568. order starting from |bucket[1]| until we find the first non-empty bucket.
  569. Note that such a bucket must exist since the case that the heap is empty
  570. after the operation was excluded by the previous |if| condition.
  571. We check all items of the found bucket to retrieve the one with
  572. the smallest key. 
  573. @<find new...@>=
  574. int idx = 1;
  575. while (buckets[idx] == nil)
  576.   idx++;
  577. item = buckets[idx];
  578. r_heap_item dummy = item->succ;
  579. while (dummy != nil) {
  580.   if ((int)dummy->key < (int)item->key)
  581.     item = dummy;
  582.   dummy = dummy->succ;
  583. }
  584. // we have found the minimum 
  585. @ We now reorganize the heap. The element that was found by the previous
  586. code segment will be the new minimum of the heap. Then we 
  587. recalculate the new bucket boundaries for all buckets with index
  588. $1 < i < idx$.
  589. Then the new minimal element is moved to |bucket[0]|, and for all
  590. the other elements in |bucket[idx]|, we scan the new bucket 
  591. boundaries starting from |u[idx-1]| in descending order and
  592. move the element to the appropriate bucket.
  593. It is worth noting that out of the newly computed bucket boundaries,
  594. at least the last one, |u[idx-1]|, equals |u[idx]|.
  595. It follows that |every| element of |bucket[idx]| must
  596. move to a bucket with smaller index. This allows us to amortize
  597. the time previously spent to find the new minimum.
  598. @<reorg...@>=
  599. set_bucket_bounds(int(item->key),idx);
  600. rm_item(item);
  601. add_item(item,0);
  602. /* Redistribution */
  603. item = buckets[idx];
  604. r_heap_item next;
  605. while (item != nil) {
  606.   next=item->succ;
  607.   /* we know that every item in bucket #idx MUST be redistributed */
  608.   rm_item(item);
  609.   int i = findbucket(item,idx);
  610.   add_item(item,i);
  611.     
  612.   item = next;
  613. }
  614.  
  615. @ The operations |size| and |empty| can be easily implemented using the 
  616. counter |si|.
  617. @<public...@>+=
  618. int r_heap::size (void) const
  619. {
  620.   return si;
  621. }
  622. int r_heap::empty (void) const
  623. {
  624.   return (si==0);
  625. }
  626. @ The following functions are used for iterating over the heap elements.
  627. There is a LEDA macro |forall_items| which is based on these functions.
  628. The function |first_item| is particularly simple because of our 
  629. heap invariant.
  630. The function |next_item| checks whether the given item has a successor
  631. in its bucket. If this is the case, that successor is returned.
  632. Otherwise, the following buckets are checked until a non-empty
  633. bucket is found. If the search is not successful, the given item
  634. is the last one and |next_item| returns |nil|. Otherwise, the first
  635. element of the found bucket is returned.
  636. @<public...@>+=
  637. r_heap_item r_heap::first_item (void) const  {
  638.   return buckets[0]; // nil if heap is empty
  639. }
  640. r_heap_item r_heap::next_item (r_heap_item p) const  {
  641.   if (p->succ != nil)
  642.     return p->succ; 
  643.   else {
  644.     int next = p->bucket + 1;
  645.     while((next < B) && (buckets[next] == nil))
  646.       next++;
  647.     if (next == B) 
  648.       return nil;
  649.     else
  650.       return buckets[next];
  651.   }
  652. }
  653. @ The following function gives a overview of the contents of an |r_heap|.
  654. It is added for maintenance purposes.
  655. @<public...@>=
  656. void r_heap::print_contents (ostream& os) const
  657. {
  658.   r_heap_item item;
  659.   os << "--------------------------------------n";
  660.   os << "Si: " << si << "n";
  661.   os << "--------------------------------------n";
  662.   for(int i=0;i<B;i++)
  663.   {
  664.     os << "--------------------------------------n";
  665.     os << "Bucket " << i << "n";
  666.     os << "Intervall: [";
  667.     if (i > 0)
  668.       os << u[i-1] - 1;
  669.     else
  670.       os << u[i];
  671.     os << "," << u[i] << "]n";
  672.     os << "--------------------------------------n";
  673.     item = buckets[i];
  674.     while (item != nil)
  675.     {
  676.       os << "Key: " << (int)item->key << " Bucket: " << item->bucket;
  677.       os << "n";
  678.       item = item->succ;
  679.     }
  680.   }
  681. }  
  682. @ Here are the header files that need to be included for the implementation
  683. of |r_heap|.
  684. @<r_heap includes@>=
  685. #include "r_heap.h"
  686. #include <math.h>
  687. @ The source code of the |r_heap| implementation is composed of the 
  688. following chunks.
  689. @(r_heap.c@>=
  690. @<r_heap includes@>@;
  691. @<constru...@>@;
  692. @<priv...@>@;
  693. @<publ...@>@;
  694. @* Dijkstra's algorithm.
  695. Here is the LEDA implementation of Dijkstra's algorithm.
  696. It uses the generic LEDA type |p_queue|. 
  697. We will use this algorithm to evaluate the performance of our
  698. |r_heap| implementation. 
  699. Note that the function |decrease_key| described in the definition 
  700. of |r_heap| is replaced by |decrease_p|. The reason for this is a LEDA 
  701. naming convention. Our implementation is integrated into the generic
  702. priority queue framework by an interface thats uses |r_heap| as an 
  703. {em implementation parameter/} for |p_queue| (see also the next section). 
  704. While this interface e.g. offers an operation |decrease_p|, it actually
  705. expects the implementation to provide the corresponding function with name 
  706. |decrease_key| and explicitly performs the switch. 
  707. More information on this issue can be found in the textbook
  708. on LEDA cite{ledabook}.
  709. @<function dijkstra@>=
  710. void dijkstra(graph&G,
  711.       node s,
  712.       edge_array<int>&cost,
  713.       node_array<int>&dist,
  714.       node_array<edge>&pred,
  715.       p_queue<int,node>&PQ) {
  716.   node_array<pq_item>I(G);
  717.   node v;
  718.   forall_nodes(v,G)  {
  719.     pred[v]= nil;
  720.     dist[v]= MAXINT;
  721.   }
  722.   dist[s] = 0;
  723.   I[s]= PQ.insert(0,s);
  724.   while(!PQ.empty())  {
  725.     
  726.     pq_item it = PQ.find_min();
  727.     node u = PQ.inf(it);
  728.     int du = dist[u];
  729.     edge e;
  730.     forall_adj_edges(e,u) {
  731.       v = G.target(e);
  732.       int c = du+cost[e];
  733.       if(c < dist[v]) {
  734.         if(dist[v] == MAXINT)
  735.           I[v] = PQ.insert(c,v);
  736.         else
  737.           PQ.decrease_p(I[v],c);
  738.         dist[v] = c;
  739.         pred[v] = e;
  740.       }
  741.     }
  742.     PQ.del_item(it);
  743.   }
  744. }
  745.   
  746. @* The benchmark program.
  747. In this section, we show a simple program that compares radix heaps 
  748. with Fibonacci heaps, LEDA's default implementation for priority queues.
  749. The declaration |p_queue<int,node>| creates a 
  750. Fibonacci heap whose priorities are integers, each of which has
  751. a |node| associated with it.
  752. An |r_heap| with maximal key span |M| is provided as a priority queue
  753. with the declaration |p_queue<int,node,r_heap>(M)|.
  754. With this mechanism, |r_heap| is provided as an 
  755. {em implementation parameter/} for the type |priority_queue|. 
  756. Inclusion of the implementation parameter as well as the replacement of
  757. the generic pointer type |GenPtr| by the
  758. actual parameter types |int| and |node| is done by the interface
  759. in the file {tt<LEDA/_,prio.h>}. See cite{ledabook} for details.
  760. @(main.c@>=
  761. @<main includes@>@;
  762. @<function dijkstra@>@;
  763. int main(void) 
  764.   GRAPH<int,int> G;
  765.   int n = read_int("# nodes = "); 
  766.   int m = read_int("# edges = ");
  767.   random_graph(G,n,m);
  768.   edge_array<int>  cost(G);
  769.   node_array<int>  dist(G);
  770.   node_array<edge> pred(G);
  771.   int M = read_int("max edge cost = ");
  772.   node s = G.choose_node();
  773.   edge e;
  774.   forall_edges(e,G) G[e] = cost[e] = rand_int(0,M);
  775.   p_queue<int,node>* PQ[2];
  776.   PQ[0] = new _p_queue<int,node,f_heap>;
  777.   PQ[1] = new _p_queue<int,node,r_heap>(M);
  778.   float T  = used_time();
  779.   float t_f = 0.0, t_r = 0.0;
  780.   dijkstra(G,s,cost,dist,pred,*(PQ[0]));
  781.   t_f = used_time(T);
  782.   dijkstra(G,s,cost,dist,pred,*(PQ[1]));
  783.   t_r = used_time(T);
  784.   cout << string("f_heap: %6.2f sec, r_heap: %6.2f secn",t_f,t_r);
  785. }
  786. @ We need to include the following header files.
  787. @<main includes@>=
  788. #include "r_heap.h"
  789. #include <LEDA/random.h>
  790. #include <LEDA/p_queue.h>
  791. #include <LEDA/_p_queue.h>
  792. #include <LEDA/graph.h>
  793. #include <LEDA/graph_alg.h>
  794. #include <fstream.h>
  795. #include <string.h>
  796. #include <math.h>
  797. @* Experiments.
  798. We ran the experiments on a SUN workstation SPARC-10. The code was compiled
  799. with the GNU C++ compiler {tt g++} and linked with the LEDA libraries 
  800. {tt lG} (graphs) and {tt lL} (basis) and the math library {tt lm}. 
  801. See the LEDA manual cite{lman}
  802. for more details on the use of the different LEDA libraries.
  803. The command line syntax to obtain the benchmark program is therefore
  804. begin{center}
  805. tt
  806. g++ -O -o r_heap r_heap.c main.c -lG -lL -lm
  807. end{center}
  808. The test results are shown in tables.
  809. Additionally, they are visualized using GNUPLOT.
  810. Our inputs are generated using a LEDA facility that produces
  811. random graphs. An arbitrary node of the test graph is chosen as the
  812. start node~$s$. Note that Dijkstra's algorithm as described above
  813. only visits the nodes reachable from~$s$. 
  814. In order to avoid biased results, we add a minimal number of edges to
  815. an input graph such that every node is reachable from~$s$.
  816. In our experiments, we varied the maximal edge costs $C$ as well as the 
  817. number of nodes and edges.
  818. We mainly examined sparse graphs since we were interested in problem
  819. instances where the theoretical time bounds for the 
  820. implementations---$O(m+nlog n)$ vs. $O(m+nlog C)$---are actually different.
  821. We ran two kinds of experiments. In the first kind, we measured the running time 
  822. against varying $C$ for three problems where $n$ and $m$ are fixed.
  823. The test cases are 
  824. (1) $n=1000,m=10000$,
  825. (2) $n=1000,m=100000$ and
  826. (3) $n=10000,m=100000$.
  827. In the second kind of tests, we measured the running time against varying $n$ 
  828. for four problems where $C$ is fixed and $m=n,f(n)$ for a given function $f(n)$.
  829. More specifically, for $C$ we tested one small value, $10$, and one large value,
  830. $10^6$. For the number of edges $m$, we also chose one small value, $3n$,
  831. and one large value, $nlog n$. Note that we consider $m=nlog n$ as large
  832. although there might be $Omega(n^2)$ edges. The reason is that 
  833. for $mgg nlog n$, choosing among the different heap implementations
  834. will no longer have a significant influence on the running time.
  835. For each experiment, there is a table showing the running times $t_f$
  836. and $t_r$ for Dijkstra's algorithm using |f_heap| and |r_heap|, respectively. 
  837. These values are also shown in the GNUPLOT graph below the table. 
  838. Additionally, the table lists the absolute and relative advantage of |r_heap| 
  839. over |f_heap|, given by $t_f - t_r$ and $frac{t_f - t_r}{t_r}cdot 100$,
  840. respectively.
  841. All times are given in seconds.
  842. newpage
  843. @ Time vs Maximal Edge Cost $C$ for $n=1000$ and $m=10000$.
  844. vfill
  845. input numdata/t_cvar-1000-10000.dat
  846. vfill
  847. input numdata/cvar-1000-10000.tex
  848. vfill
  849. newpage
  850. @ Time vs Maximal Edge Cost $C$ for $n=1000$ and $m=100000$.
  851. vfill
  852. input numdata/t_cvar-1000-100000.dat
  853. vfill
  854. input numdata/cvar-1000-100000.tex
  855. vfill
  856. newpage
  857. @ Time vs Maximal Edge Cost $C$ for $n=10000$ and $m=100000$.
  858. vfill
  859. input numdata/t_cvar-10000-100000.dat
  860. vfill
  861. input numdata/cvar-10000-100000.tex
  862. vfill
  863. newpage
  864. @ Time vs number of nodes $n$ for $C=10$ and $m=3n$.
  865. vfill
  866. input numdata/t_nvar-10-3n.dat
  867. vfill
  868. input numdata/nvar-10-3n.tex
  869. vfill
  870. newpage
  871. @ Time vs number of nodes $n$ for $C=10$ and $m=nlog n$.
  872. vfill
  873. input numdata/t_nvar-10-log.dat
  874. vfill
  875. input numdata/nvar-10-log.tex
  876. vfill
  877. newpage
  878. @ Time vs number of nodes $n$ for $C=10^6$ and $m=3n$.
  879. vfill
  880. input numdata/t_nvar-10e06-3n.dat
  881. vfill
  882. input numdata/nvar-10e06-3n.tex
  883. vfill
  884. newpage
  885. @ Time vs number of nodes $n$ for $C=10^6$ and $m=nlog n$.
  886. vfill
  887. input numdata/t_nvar-10e06-log.dat
  888. vfill
  889. input numdata/nvar-10e06-log.tex
  890. vfill
  891. @* Implementation notes.
  892. This paper emerged from a semester project associated with a course
  893. on data structures and algorithms taught by Kurt Mehlhorn and Christian Schwarz
  894. at the Univerisit"at des Saarlandes in the winter semester of 1994/95,
  895. with teaching assistants Christoph Schmitz and Frank Schulz.
  896. Given the specification {tt r_heap.h}, and the application 
  897. framework consisting of the code for Dijkstra's algorithm, the test program 
  898. and some test graphs, the students were asked to implement |r_heap| and
  899. to conduct a performance comparison against |f_heap| using the test program.
  900. The students were supposed to form small groups for this task.
  901. Combining our own ideas with those provided by the solutions of the student
  902. teams and those resulting from discussions on the project, we came up with
  903. the final solution that is presented in this paper.
  904. Our solution is mainly based on work of the group formed by 
  905. Jochen K"onemann,
  906. Arnd Christian K"onig and 
  907. Mirek Riedewald. 
  908. Additionally, the contributions of all other students who worked on the 
  909. project helped to shape this final version. These students are
  910. input proj-names
  911. We thank them all.
  912. @ {bf References.}
  913. vspace*{-1.3cm}
  914. defrefname{} 
  915. begin{thebibliography}{IEE87}
  916. bibitem[MN95]{ledabook}K. Mehlhorn, S. N"aher.
  917. {sl The LEDA platform for combinatorial and geometric computing.}
  918. In preparation.
  919. bibitem[MN89]{ledaMN}K. Mehlhorn, S. N"aher.
  920. {sl LEDA: A library of efficient data types and algorithms.}
  921. LNCS, vol. 379, pp. 88-106, 1989, full version to appear in CACM.
  922. bibitem[N"ah93]{lman}S. N"aher.
  923. {sl LEDA manual.}
  924. Technical report MPI-I-93-109, Max-Planck-Institut f"ur Informatik, Saarbr"ucken, 1993.
  925. bibitem[AMOT90]{rheap}R. Ahuja, K. Mehlhorn, J. Orlin and R. Tarjan. 
  926. {sl Faster Algorithms for the shortest path problem.} 
  927. Journal of the ACM, Vol.37, 1990, pp.213-223.
  928. bibitem[FT87]{fheap}M. Fredman and R.E. Tarjan. 
  929. {sl Fibonacci heaps and their uses in improved network optimization algorithms.}
  930. Journal of the ACM, Vol.34, 1987, pp. 596-615.
  931. bibitem[D59]{dijkstra}E. Dijkstra. 
  932. {sl A note on two problems in connexion with graphs.}
  933. Numer. Math. 1, 1959, pp. 269-271.
  934. end{thebibliography}
  935. end{document}