pq *create_pq(void)
pq
,
empty for the moment, boolean pq_is_empty(pq *p)
TRUE
if the priority queue is empty and returns
FALSE
otherwise,
pq_object *find_min(pq *p)
void insert(pq *p, pq_object *objectptr, cost object_cost);
void delete_min(pq *p);
void reduce_cost(pq *p, pq_object *objectptr, cost smaller_cost);
smaller_cost
that is smaller than the current cost of the object, reduces
the cost of the object to smaller_cost
.
pq_object
and cost
are
provided by the ADT user; the data type pq
is chosen by
the designer of the data structure that implements the ADT. In C, the
data type boolean
is not provided explicitly, but may be
created by the definitions
#define TRUE 1 #define FALSE 0 typedef char boolean;In implementing Priority Queue, we will assume that the data type
pq_object
is a structure including an element
pq_cost
of type cost
and that the ADT designer
may choose to include additional elements in this structure.
typedef struct list_item { pq_object *content; struct list_item *next; } list_item; typedef list_item pq; pq *create_pq(void) { pq *p; p = (pq *) malloc(sizeof(pq)); if(p==NULL) { printf("OUT OF MEMORY!\n"); exit(1); } p->next=NULL; return p; } boolean pq_is_empty(pq *p) { return( (p->next==NULL) ? TRUE : FALSE ); } pq_object *find_min(pq *p) { list_item *winner, *candidate; if(pq_is_empty(p)==TRUE) { printf("TRIED TO FIND A MIN-OBJECT IN AN EMPTY PRIORITY QUEUE!\n"); exit(1); } winner=p->next; for(candidate=p->next->next; candidate; candidate=candidate->next) { if(candidate->content->pq_cost < winner->content->pq_cost) winner=candidate; } return winner->content; } void pq_insert(pq *p, pq_object *object, cost object_cost) { list_item *new_item; new_item = (list_item *) malloc(sizeof(list_item)); if(new_item==NULL) { printf("OUT OF MEMORY!\n"); exit(1); } object->pq_cost=object_cost; new_item->content=object; new_item->next=p->next; p->next=new_item; } void delete_min(pq *p) { list_item *winner, *candidate, *previous; if(pq_is_empty(p)==TRUE) { printf("TRIED TO FIND A MIN-OBJECT IN AN EMPTY PRIORITY QUEUE!\n"); exit(1); } winner=p->next; for(candidate=p->next->next; candidate; candidate=candidate->next) { if(candidate->content->pq_cost < winner->content->pq_cost) winner=candidate; } for(previous=p; previous->next != winner; previous=previous->next); previous->next=winner->next; free(winner); } void reduce_cost(pq *p, pq_object *object, cost smaller_cost) { object->pq_cost=smaller_cost; }
n
nodes is
the binary tree with nodes 0,1,...,n-1
such that the
parent of each node j
other than 0
is the
floor of (j-1)/2
(and such that 0
is the
root of the tree). A binary heap is a heap structure of
pointers to the objects in the priority queue that satisfies
the min-heap condition,
heap[x]->pq_cost <= heap[y]->pq_cost
whenevery
is a descendant ofx
.
In the objects of the priority queue, we shall maintain integer
elements pq_location
that reverse the pointers stored in
the heap: if x
is a nonnegative integer smaler than
n
, then
and ifheap[x]->pq_location
equalsx
y
is a pointer stored in the heap, then
We shall maintain a pointer to the root of the heap and define the data typeheap[y->pq_location]
equalsy
.
pq
by
typedef struct pq { pq_object **heap; int capacity; int count; } pq;much as we defined the data type
stack
when implementing
the ADT Stack as an array. In fact, we implement
create_pq
just likecreate_stack
except that pq_is_empty
just likestack_is_empty
;
find_min
just liketop_of_stack
, except thatfind_min
reaches for the leftmost cell,
heap[0]
, of the array whentop_of_stack
would reach for the rightmost cell,
s[count-1]
:
pq *create_pq(void) { pq *p; pq_object **object; p = (pq *) malloc(sizeof(pq)); if(p==NULL) { printf("OUT OF MEMORY!\n"); exit(1); } object = (pq_object **) malloc(sizeof(pq_object *)); if(object==NULL) { printf("OUT OF MEMORY!\n"); exit(1); } p->heap = object; p->count = 0; p->capacity = 1; return p; } boolean pq_is_empty(pq *p) { return( (p->count==0) ? TRUE : FALSE ); } pq_object *find_min(pq *p) { if(pq_is_empty(p)==TRUE) { printf("TRIED TO FIND A MIN-OBJECT IN AN EMPTY PRIORITY QUEUE!\n"); exit(1); } return *(p->heap); }
pq_insert
and reduce_cost
rely on the function
void bubbleup(pq_object *heap[], int n, int vacant, pq_object *missing)
which is much like the function
bubbleup(heap,n,vacant,missing)
in
Heapsort(with "small" and "big" reversed):
void bubbleup(pq_object *heap[], int n, int vacant, pq_object *missing) { int parent=(vacant-1)/2; while(vacant>0 && missing->pq_cost < heap[parent]->pq_cost) { /* the actual value of heap[vacant] is irrelevant; if heap[vacant] were overwritten with missing, then the min-heap condition would be satisfied everywhere except at j=vacant */ heap[vacant]=heap[parent], heap[vacant]->pq_location=vacant; vacant=parent, parent=(vacant-1)/2; } heap[vacant]=missing, heap[vacant]->pq_location=vacant; } void pq_insert(pq *p, pq_object *object, cost object_cost) { pq_object **newheap; if(p->count == p->capacity) /** DOUBLE THE LENGTH OF THE HEAP **/ { (p->capacity)*=2; newheap = (pq_object **) realloc(p->heap, (p->capacity)*sizeof(pq_object *)); if(newheap) p->heap = newheap; else { printf("OUT OF MEMORY!\n"); exit(1); } } object->pq_cost=object_cost; (p->count)++; bubbleup(p->heap, p->count, (p->count)-1, object); } void reduce_cost(pq *p, pq_object *object, cost smaller_cost) { object->pq_cost=smaller_cost; bubbleup(p->heap, p->count, object->pq_location, object); }
delete_min
relies on the function
void siftdown_from_root(pq_object *heap[], int n, pq_object *missing)
which is much like the function
siftdown(heap,n,0,missing)
in
Heapsort (with "small" and "big" reversed):
void siftdown_from_root(pq_object *heap[], int n, pq_object *missing) { int vacant=0, child=2; while(child<n) { /* the actual value of heap[vacant] is irrelevant; the min-heap condition is satisfied everywhere except possibly at i=vacant or j=vacant */ if(heap[child-1]->pq_cost < heap[child]->pq_cost) child--; heap[vacant]=heap[child], heap[vacant]->pq_location=vacant; vacant=child, child=2*(vacant+1); } if(child==n) { heap[vacant]=heap[n-1], heap[vacant]->pq_location=vacant; vacant=n-1; } bubbleup(heap, n, vacant, missing); } void delete_min(pq *p) { (p->count)--; siftdown_from_root(p->heap, p->count, *((p->heap)+(p->count))); }A nice animation of a sequence of operations
pq_insert
and
delete_max
in the related data structure max-heap
(where "small" and "big" are reversed) has been
written by Woi Ang.
A pairing heap is an ordered tree of pointers to the objects; the tree is represented by a pair of pointers for each of its nodes,
node.down
that points to the leftmost child of
node
NULL
if and only if node
is a leaf) and
node.across
that points to the immediate successor of
node
in the list of its siblingsNULL
if and only if node
is either the
root of the tree or the rightmost child of its parent);
node.content
that point to objects in the priority queue
x.content->pq_cost <= y.content->pq_cost
whenevery
is a child ofx
.
In the objects of the priority queue, we shall maintain elements
pq_location
that point to the tree nodes of the pairing heap
and reverse the pointers content
:
if x
points to a tree node, then
and ifx->content->pq_location
equalsx
y
points to an object in the priority queue, then
We shall maintain a pointer to the root of the pairing heap and define the data typey->pq_location->content
equalsy
.
pq
as a pointer to a tree-node.
create_pq
constructs a pointer to a
tree-node, sets its value at NULL
and returns a pointer
to this pointer:
pq *create_pq(void) { pq *p; p = (pq *) malloc(sizeof(pq)); if(p==NULL) { printf("OUT OF MEMORY!\n"); exit(1); } *p=NULL; return p; }
The access function pq_is_empty
, given a pointer
p
to a pointer *p
to the root of a pairing
heap, returns TRUE
if the pairing heap is empty and
returns FALSE
otherwise; the access function
find_min
, given a pointer p
to a pointer
*p
to the root of a nonempty pairing heap, returns a
pointer to the object in the priority queue that the root of the
pairing heap points to.
boolean pq_is_empty(pq *p) { return( (*p==NULL) ? TRUE : FALSE ); } pq_object *find_min(pq *p) { if(pq_is_empty(p)==TRUE) { printf("TRIED TO FIND A MIN-OBJECT IN AN EMPTY PRIORITY QUEUE!\n"); exit(1); } return (*p)->content; }
link
will make one of the two roots the
leftmost child of the other, so as to create a single pairing heap, and
it will return a pointer to the root of this resulting pairing heap.
tree_node *link(tree_node *a, tree_node *b) { if(a->content->pq_cost < b->content->pq_cost) { b->across=a->down; a->down=b; a->across=NULL; return a; } else { a->across=b->down; b->down=a; b->across=NULL; return b; } }
pq_insert
, given a pointer
p
to a pointer *p
to the root of a pairing
heap, given a pointer object
to an object, and given the
cost object_cost
of this object, inserts the object into
the priority queue.
void pq_insert(pq *p, pq_object *object, cost object_cost) { tree_node *new_node; new_node = (tree_node *) malloc(sizeof(tree_node)); if(new_node==NULL) { printf("OUT OF MEMORY!\n"); exit(1); } new_node->content=object; new_node->down=NULL; new_node->across=NULL; object->pq_location=new_node; object->pq_cost=object_cost; *p = (pq_is_empty(p)==TRUE ? new_node : link(*p,new_node)); }
reduce_cost
, given a pointer
p
to a pointer *p
to the root of a pairing
heap, given a pointer object
to an object, and given a
cost value smaller_cost
that is smaller than the current
cost of the object, reduces the cost of the object to
smaller_cost
. When the object is pointed to from the root of the
pairing heap,
reduce_cost(p, object, smaller_cost)
amounts to setting
object->pq_cost=smaller_cost;
in all other cases,
object->pq_location
that
points to the object is removed from the list of its siblings and then
object->pq_location
is reinserted into the pairing heappq_insert(p, object, smaller_cost)
except thatnew_node->down
is set at
object->pq_location->down
rather than at NULL
.
object->pq_location->content=NULL;
this signals the fact that node object->pq_location
has
turned into garbage, whose presence in the list of its siblings will
be disregarded.
void reduce_cost(pq *p, pq_object *object, cost smaller_cost) { tree_node *new_node; object->pq_cost=smaller_cost; if(object->pq_location!=*p) { new_node = (tree_node *) malloc(sizeof(tree_node)); if(new_node==NULL) { printf("OUT OF MEMORY!\n"); exit(1); } new_node->content=object; new_node->down=object->pq_location->down; new_node->across=NULL; object->pq_location->content=NULL; object->pq_location=new_node; *p = link(*p,new_node); } }
delete_min
, given a pointer
p
to a pointer *p
to the root of a nonempty
pairing heap, removes the root as well as all the children of the root
that have turned into garbage and, by repeated linking of the
remaining children of the root, combines the pairing heaps rooted at
these children into a single pairing heap. To separate the children of
the root that should be removed as meaningless (since their pointers
content
are NULL
) from the children of the
root that should be kept as meaningful (since their pointers
content
point at objects in the priority queue), we will
use a function find_sibling
. This function, given a pointer
child
to a (meaningless or meaningful) child of the root,
either
child, child->across, child->across->across, ...
NULL
to signify that there is is no
meaningful child of the root in the sequencechild, child->across, child->across->across, ...
tree_node *find_sibling(tree_node *child) { tree_node *memo; while(child && child->content==NULL) { memo=child, child=child->across; free(memo); } return child; }The linking of the meaningful children of the root proceeds in two phases. In the first phase,
void delete_min(pq *p) { tree_node *a, *b, *memo; if(pq_is_empty(p)==TRUE) { printf("TRIED TO DELETE A MIN-OBJECT FROM AN EMPTY PRIORITY QUEUE!\n"); exit(1); } a=find_sibling((*p)->down); (*p)->down = NULL; while(a) { b=find_sibling(a->across); if(b) { memo=find_sibling(b->across); a=link(a,b); a->across=(*p)->down, (*p)->down=a; a=memo; } else { a->across=(*p)->down, (*p)->down=a; break; } } if((*p)->down) { for(a=(*p)->down->across; a; a=memo) { memo=a->across; (*p)->down=link((*p)->down,a); } } memo=*p; *p=(*p)->down; free(memo); }