1: // timerq_tc.H -- generic leftist heap for priority/timer queues, etc.  Rj Brown, 08/13/93.

  3: #ifndef __timerq_tc__H
  4: #define __timerq_tc__H

  6: #include <sys/types.h>


  9: // This header file implements a generic template for leftist heaps of objects.  A
 10: // leftist heap is a particularly efficient way to implement a priority queue or timer
 11: // queue.  It is also useful for determining which element is an extremum (maximum or
 12: // minimum) of a set, or even the n least or n greatest elements of a set.
 13: // 
 14: // A leftist heap is implemented as a special kind of binary tree.  Its definition
 15: // guarantees that the root of the tree contains the extreme value member of the set of
 16: // objects in the tree.  This is also true for each of the sub-trees of the root, etc.
 17: // Furthermore, it is guaranteed that the shortest path from any node to a terminal, or leaf,
 18: // node may be found by following the right subtree.  This causes the tree to become larger
 19: // on the left side than the right, hence the name "leftist tree" -- it "leans to the left".
 20: // 
 21: // The leftist heap is very good for merging 2 leftist heaps together to form a new
 22: // leftist heap that contains all the members of the 2 original heaps.  The merge can be
 23: // done in O(log n) time, which is faster than a simple linear list for all but the smallest
 24: // of sets.  For details of the theory and implementation of leftist heaps, and complexity
 25: // and timing analysis, see:
 26: // 
 27: // Brown, R. J. 1987.  An efficient algorithm for large priority queues. DDJ June 1987.
 28: // 
 29: // Crane, C. A. 1972.  Linear lists and priority queues as balanced binary trees.  Tech. Rep.
 30: //                     STAN-CS-72-259, Computer Science Dept., Stanford Univ. Stanford, CA.
 31: // 
 32: // Knuth, D. E. 1973.  The Art of Computer Programming, Vol 3: Sorting and Searching.  
 33: //                     Addison-Wesley, Reading, MA.
 34: // 
 35: // Tarjan, R. E. 1983. Data Structures and Network Algorithms.  SIAM, Philadelphia, PA.


 38: // A node to control each element in the queue.

 40: template<class data_c>  class node_tc{

 42:   // FIXME
 43:   // This must eventually use an over-ridden "new" and "delete" that
 44:   // allocates and releases using a pool_tc<node_tc<data_c*> > instead of a
 45:   // call to "malloc" and "free" with the default "new" and "delete".

 47: public:

 49:   node_tc<data_c>* parent;                        // back pointer to parent of this node
 50:   node_tc<data_c>* left;                          // pointer to left sub-tree
 51:   node_tc<data_c>* right;                         // pointer to right sub-tree
 52:   data_c* data;                                   // pointer to users data
 53:   int dist;                                       // minimum distance to a leaf
 54:   
 55:   // CONSTRUCTOR
 56:   //
 57:   //   Wrap a data element up in a tree node.
 58:   //   This effectivly makes a priority queue of one element.

 60:   node_tc(data_c* dp) {
 61:     data = dp;                                    // hook up to data element
 62:     parent = NULL;                                // has no parent
 63:     left = NULL;                                  // has no subtrees
 64:     right = NULL;
 65:     dist = 1;                                     // distance to leaf is 1 for leaf-most nodes
 66:                                                   // (NULL has distance of 0)
 67:   }
 68:    

 70:   // DESTRUCTOR
 71:   //
 72:   //   We need to fix any pointers to the soon-to-be-deceased node so that
 73:   //   they are NULL.  This will help prevent dangling references.
 74:   //
 75:   //   A problem could be that since we cut off the left and right kids, that 
 76:   //   a memory leak could occur if they had nobody else pointing to them.
 77:   //   Oh well, that's why Lisp has a garbage collector!  In stupid C++ you
 78:   //   must keep track of these nits...
 79:   //
 80:   //   FIXME  Should we throw an error if the kids are *NOT* NULL?

 82:   ~node_tc() {

 84:     if (parent != NULL) {                         // does this node even have a parent?
 85:       if (parent->left == this) {                 // yes, is it seated on the left hand?
 86:         parent -> left = NULL;                    // yes, cut it off!
 87:       } else if (parent->right == this) {         // or on the right hand?
 88:         parent -> right = NULL;                   // yes, cut it off!
 89:       }                                           // should we throw an error otherwise?
 90:       // FIXME  What if it is seated on *BOTH* sides !?!
 91:     }
 92:     
 93:     if (left != NULL) {                           // any left kids to cut off?
 94:       if (left->parent == this) {                 // yes, do they claim us as parents?
 95:         left->parent = NULL;                      // yes, cut them off!
 96:       }                                           // should we throw an error otherwise?
 97:     }
 98:     
 99:     if (right != NULL) {                          // any right kids to cut off?
100:       if (right->parent == this) {                // yes, do they claim us as parents?
101:         right->parent = NULL;                     // yes, cut them off!
102:       }
103:     }                                             // should we throw an error otherwise?
104:     // FIXME  What about danglers in the data field ???
105:   }
106:   

108:   // Return the distance from a given node to a leaf.
109:   //
110:   //   This transparently handles case of NULL pointer to node.
111:   
112:   int distance(node_tc<data_c>* p) {
113:     if (p == NULL) {                              // if node is non-existent,
114:       return 0;                                   // then it has a distance of zero,
115:     } else {                                      // otherwise
116:       return p->dist;                             // it has the distance that p points to.
117:     }
118:   }
119:   
120:   
121:   // TREE TRAVERSAL
122:   //
123:   // The following routine traverses a binary tree pointed to by p, visiting each node
124:   // by deleting it, but only after deleting its subtrees recursively.
125:   //
126:   // See the folowing for details on the implementation and analysis of this tree
127:   // traversal algorithm:
128:   //
129:   // Knuth D. E. 1969.  The Art of Computer Programming, Vol 1.  Fundamental Algorithms,
130:   //                    Addison Wessley, Reading, MA.
131:   
132:   // Knuths end-order tree traversal
133:   
134:   void end_order_delete(void) {

136:     if (this == NULL) {                           // if there is no tree at all,
137:       return;                                     // then do nothing!
138:     }

140:     left->end_order_delete();                     // traverse the left subtree
141:     right->end_order_delete();                    // traverse the right subtree
142:     
143:     delete this;                                  // delete this node
144:   }
145:   
146:   
147:   // Merge 2 priority/timer queues.
148:   //
149:   //   This routine only merges the 2 right legs by descending down them;
150:   //   it does not balance the tree, or fix up the distances!
151:   //   It returns a pointer to the leaf-most node affected.
152:   //   The "balance" routine must be called after "merge" to fix up the tree.
153:   //
154:   //   This "merge" routine, together with the "balance" routine, implement an
155:   //   iterative algorithm for combining 2 leftist heaps.  This is similar
156:   //   to the algorithm given by Knuth, as contrasted with the recursive
157:   //   algorithm given by Tarjan.  The implementation given here takes
158:   //   advantage of the presence of the "parent" back-pointers to simplify
159:   //   the balancing part of the process, whereas Knuth does not assume the
160:   //   presence of back-pointers.  We need the back-pointers to perform
161:   //   the deletions needed to "advance" a node in the heap.  The iterative
162:   //   implementation is prefered because its execution speed is faster,
163:   //   although the recursive implementation is somewhat easier to understand.
164:   
165:   // (p->data->precedes(q->data, p->data))
166:   // (p->data->precedes(r->data, q->data))
167:   
168:   boolean in_order(node_tc<data_c>* a, node_tc<data_c>* b) { // are nodes *a and *b in proper order?
169:     
170:     if(b == NULL) {                               // if there ain't no b, then a's gotta be first!
171:       return TRUE;
172:     }
173:     
174:     if(a == NULL) {                               // we've got an a, but no b, so they're backwards!
175:       return FALSE;
176:     }
177:     
178:     return a->data->precedes(a->data, b->data);   // we gotta do this one the HARD way!
179:   }


182:   node_tc<data_c>* merge(node_tc<data_c>* p, node_tc<data_c>* q) {
183:     node_tc<data_c>* r;                           // a temporary pointer to a node
184:     boolean precedes(data_c* p, data_c* q);
185:     
186:     if (q == NULL) {                              // is q empty?
187:       return p;                                   // yes, p is the answer, return
188:     }
189:     
190:     while (p != NULL) {                           // for as long as p exists...
191:       
192:       // Make sure p precedes q.  If not, fix it!
193:       
194:       if (!in_order(p, q)) {                      // does p come before q?
195:         r = p;                                    // no, we want p to be the earliest
196:         p = q;                                    // so swap p
197:         q = r;                                    // with q
198:       }
199:       
200:       // Now we have p > q.  By definition p > r.  Check if p > q > r.  If so, insert q between p and r.
201:       
202:       r = p->right;                               // descend down right side of tree leafwards
203:       
204:       if (in_order(q, r)) {                       // does q come before p's rite subtree?
205:         p->right = q;                             // yes, insert q 
206:         q->parent = p;                            // between p and r
207:       }
208:       
209:       p = r;                                      // move down the right branch
210:     }
211:     
212:     return q;                                     // return the last node affected
213:   }
214:   
215:   
216:   // Balance the tree resulting from merge above, and fix up distances.
217:   //   This routine ascends up the tree produced by merge.
218:   //   The argument is the end of the merge, as returned by merge.
219:   //   The "balance" routine returns a pointer to the balanced tree.
220:   
221:   node_tc<data_c>* balance(node_tc<data_c>* q) {
222:     node_tc<data_c>* p = NULL;                    // temporary pointer
223:     
224:     while (q != NULL) {                           // are we at the root yet?
225:       p = q;                                      // no, work on this node
226:       
227:       if (distance(p->left) < distance(p->right)) { // is this node out of balance?
228:         q = p->left;                                // yes, 
229:         p->left = p->right;                         // swap left and
230:         p->right = q;                               // right subtrees,
231:       }
232:       
233:       p->dist = distance(p->right) + 1;           // fix up distance
234:       q = p->parent;                              // ascend up the tree root-wards
235:     }
236:     
237:     return p;                                     // return pointer to root of tree
238:   }
239:   
240:   
241:   // combine 2 priority/timer queues into 1 queue
242:   
243:   node_tc<data_c>* combine(node_tc<data_c>* p, node_tc<data_c>* q) {
244:     return balance(merge(p, q));                  // return result of balancing the merger of the 2 queues
245:   }
246:   
247: };                                                /* end class node_tc */


250: template<class data_c> class timerq_tc {
251:   
252: private:                                          // Private Instance Variables
253:   
254:   // The list head for the leftist heap.
255:   
256:   node_tc<data_c>* tree;                          // pointer to leftist tree
257:   fifo_tc<node_tc<data_c> >* batch_que;           // pointer to batchification queue
258:   
259:   
260: public:                                           // Public Methods
261:   
262:   // Constructor
263:   
264:   timerq_tc() {
265:     tree = NULL;                                  // tree is initially empty
266:   }
267:   
268:   
269:   // Destructor
270:   
271:   ~timerq_tc() {
272:     tree->end_order_delete();                     // delete all the nodes & data items
273:   }
274:   
275:   
276:   // Return a pointer to the head data item on a queue.
277:   
278:   data_c* queue_head() {
279:     if (tree == NULL) {                           // if no queue, 
280:       return NULL;                                // return NULL
281:     } else {                                      // otherwise,
282:       return tree->data;                          // return pointer to users data
283:     }
284:   }
285:   
286:   
287:   // enqueue a user data element on a queue
288:   //   returns a pointer to the queue node
289:   
290:   node_tc<data_c>* enque(data_c* dp) {
291:     node_tc<data_c>* p = new node_tc<data_c>(dp); // wrap up user data as a 1 element heap
292:     
293:     tree = tree->node_tc<data_c>::combine(p, tree); // combine new element with old tree
294:     
295:     return p;                                     // return pointer to new element
296:   }
297:   
298:   
299:   // dequeue the head element from the queue
300:   
301:   data_c* deque() {
302:     node_tc<data_c>* tp = tree;                   // pointer to node at head of queue
303:     node_tc<data_c>* lp = tp->left;               // pointer to left subtree
304:     node_tc<data_c>* rp = tp->right;              // pointer to right subtree
305:     data_c* dp = tp->data;                        // pointer to data at head of queue

307:     if (lp != NULL) {                             // if the is a left kid,
308:       lp->parent = NULL;                          // he forgets that the dequeued node
309:     }                                             // was his parent

311:     if (rp != NULL) {                             // if the is a right kid,
312:       rp->parent = NULL;                          // he forgets that the dequeued node
313:     }                                             // was his parent

315:     tree = tp->combine(tp->left,                  // make tree less root by combining left
316:                        tp->right);                // and right subtrees

318:     delete tp;                                    // get rid of the tree node
319:                                                   // that held the dequeued user data
320:     
321:     return dp;                                    // return user data removed from head of queue
322:   }
323:   
324:   
325:   // Advance an element towards the head of the queue.
326:   //   This routine operates upon a node whose key has been modified.
327:   //   The assumptions are:
328:   //     1.  The new key precedes the old key.  This means that both the left
329:   //         and right subtrees are therefore still in proper precedence order
330:   //         with respect to the affected node.  The affected node, together
331:   //         with both its left and right subtrees, now needs to be moved
332:   //         towards the root of the tree.
333:   //     2.  The "advance" routine is called after the affected node has had
334:   //         its key altered, but *BEFORE* any other operations are performed
335:   //         on the queue.  This is because after the key has been altered,
336:   //         the queue is in an essentially corrupt state.  This corruption
337:   //         is corrected by calling "advance".  Note especially that no
338:   //         premption should be permitted between altering the key and calling
339:   //         "advance", since one cannot predict what other processes may do
340:   //         regarding the queue.  This "without premption" condition will be
341:   //         satisfied in CTES if the modification to the ley is made and then
342:   //         the "advance" call made without allowing exit from the current CTES
343:   //         process, since the CTES exec is non-pre-emptive.
344:   //     3.  This routine would perhaps be better formulated if it actually took
345:   //         as an argument the new value to assign to the key, but then it would
346:   //         need to be a template of its own, since the leftist heap routines
347:   //         have no knowledge of the types of data records and keys being manipulated.
348:   
349:   void advance(node_tc<data_c>* p) {
350:     node_tc<data_c>* q;
351:     
352:     if (p == NULL) {                              // is there any node to advance?
353:       return;                                     // no, just return
354:     }
355:     
356:     q = p->parent;                                // point to the nodes parent
357:     if (q==NULL) {                                // is the node already the head of the queue?
358:       return;                                     // yes, just return
359:     }
360:     
361:     if (q->left == p) {                           // are we our parents left child?
362:       q->left = NULL;                             // yes, emancipate ourselves
363:     } else {                                      // no, then we must be the right child!
364:       q->right = NULL;                            // emancipate ourselves
365:     }
366:     
367:     tree = tree->combine(tree, p);                // put the tree back together again
368:   }
369:   
370:   
371:   // put a user data element into a batch to be queued later
372:   //   returns a pointer to the queue node
373:   
374:   node_tc<data_c>* batchify(data_c* dp) {
375:     node_tc<data_c>* p = new node_tc<data_c>(dp); // wrap up the data in a tree node
376:     
377:     (void) batch_que->enque(p);                   // put that node on the batch fifo queue
378:     
379:     return p;                                     // return a pointer to the tree node
380:   }
381:   
382:   
383:   // turn a batch into a heap
384:   //  merges new heap with old queue to produce new queue
385:   
386:   void heapify(void) {
387:     node_tc<data_c>* p = NULL;
388:     node_tc<data_c>* q = NULL;
389:     
390:     // loop to successively combine heaps until only 1 remains.
391:     
392:     while (TRUE) {
393:       
394:       p = batch_que->deque();                     // get a little heap
395:       if (p == NULL) {                            // is it empty?
396:         p = q;                                    // yes, use the other heap
397:         break;                                    // drop out of this loop
398:       }
399:       
400:       q = batch_que->deque();                     // get another little heap
401:       if (q == NULL) {                            // is it empty?
402:         break;                                    // yes, drop out of this loop
403:       }
404:       
405:       (void) batch_que->enque(p->combine(p, q));  // combine the 2 little heaps,
406:       // and put bigger heap back on fifo
407:       
408:     }                                             /* end loop combine_heaps_on_fifo */
409:     
410:     tree = p;                                     // result is the new priority queue
411:   }
412:   
413: };                                                /* end class timerq_tc */

415: #endif                                            // #ifndef __timerq_tc__H