/************************************* * heap.c * * See the notes, API, and license in heap.h * * Growth of the heap when called push_node(n) isn't entirely implemented; * at the moment the maximum heap size is the first multiple * of HEAP_BLOCK_SIZE bigger than the array passed into new_heap(). * * $Id: heap.c 12567 2007-04-09 16:12:05Z mahoney $ ***********************************/ #include #include #include "utility.h" #include "heap.h" // These macros to implement the heap tree-to-array mapping that // Levitin's text describes, namely // If H is h.nodes, then // (1) H[0] is ignored. // (2) H[1..n] are heap entries. // (3) children of H[i] are H[2*i] and H[2*i+1] // (4) parent of H[j] is H[(int)(i/2)] #define i_parent(i) ((int)((i)/2)) #define i_left(i) (2*(i)) #define i_right(i) (1+2*(i)) // More macros to save typing and make the code clearer. All assume // "h" is the heap, which must defined before the macro is called. // The meaning of the variable names is : // n number of nodes in heap // (ip, wp) (index, weight) of parent // (ic, wp) (index, weight) of child #define weight(i) (h->nodes[(i)]->weight) #define wrong(wp,wc) (h->descending ? ((wp) < (wc)) : ((wp) > (wc))) #define bad_child(ip,ic,n) (((ic)<=(n)) && ! wrong(weight(ic), weight(ip))) #define HEAP_BLOCK_SIZE 1024 struct node_struct { void* data; int weight; }; struct heap_struct { int size; // number of nodes in the heap int space; // sizeof(nodes) bool descending; // true => parent>child; false => parentnodes satisfies the heap condition. bool heap_check(heap h){ int i; int n = h->size; for (i=1; i<=n/2; i++){ // loop over parents if (bad_child(i, i_left(i), n)){ return FALSE; } if (bad_child(i, i_right(i), n)){ return FALSE; } } return TRUE; } // If the node with index k is out of order with respect to its children, // sift it down the heap tree until it's positioned correctly. void sift_down(heap h, int k){ bool is_heap = FALSE; node v = h->nodes[k]; int j; while ((! is_heap) && (2*k <= h->size)){ j = 2*k; if (j < h->size){ if (wrong(weight(j), weight(j+1))){ j++; } } if (! wrong(v->weight, weight(j))){ is_heap = TRUE; } else { h->nodes[k] = h->nodes[j]; // put node j into k'splace k = j; } step_counter++; // only for measuring algorithm performance } h->nodes[k] = v; // put node v in k's place, } // Turn the array of nodes in heap h into a heap. void heapify(heap h){ // HeapBottomUp, pg 226 Levitin's "Design & Analysis of Algorithms, 2nd ed" int i; for (i=h->size/2; i>0; i--){ sift_down(h, i); } } void print_heap(heap h){ int i; printf("heap{ size=%i space=%i descending=%i \n data=(", h->size, h->space, h->descending); for (i=1; i<=h->size; i++){ printf("%i ", h->nodes[i]->weight); } printf(") }\n"); } heap new_heap(int n_nodes, node* nodes, bool descending){ int i; heap h = (heap) safe_malloc(sizeof(struct heap_struct)); h->size = n_nodes; h->descending = descending; h->space = (1 + n_nodes/HEAP_BLOCK_SIZE) * HEAP_BLOCK_SIZE; h->nodes = (node*) safe_malloc(h->space * sizeof(struct node_struct)); h->nodes[0] = NULL; // position 0 isn't used. for (i = 0; i < n_nodes; i++) { h->nodes[i+1] = nodes[i]; } for (i = n_nodes+1; i < h->space; i++){ h->nodes[i] = NULL; } heapify(h); return h; } node new_node(int weight, void* data){ node n = (node) safe_malloc(sizeof(struct node_struct)); n->weight = weight; n->data = data; return n; } node pop_heap(heap h){ // pg 227 Levitin node root = h->nodes[1]; // remember root of tree if (h->size > 0){ h->nodes[1] = h->nodes[h->size]; // move smallest to root h->nodes[h->size] = NULL; h->size--; // decrease size of tree sift_down(h, 1); // sift new root down to proper place } return root; } void push_heap(heap h, node n){ // pg 226 Levitin int k; node tmp; if (h->size >= h->space-1){ // Need to grow allocated heap array before more elements can be added, // probably by HEAP_BLOCK_SIZE using realloc, malloc, etc. // I haven't done this yet; for now we just crash with an error exit. // A quick fix if you hit this is to put HEAP_BLOCK_SIZE to something // bigger than the maximum number of elements you need in your heap. exit(1); } h->size++; k = h->size; h->nodes[k] = n; // 1. Add node at end. while (k != 1 && // 2. While not root and wrong(weight(i_parent(k)), weight(k))){ // out of order, tmp = h->nodes[k]; // swap upwards. h->nodes[k] = h->nodes[i_parent(k)]; h->nodes[i_parent(k)] = tmp; // printf(" k, i_parent(k), weight(k) = %i, %i, %i \n", // k, i_parent(k), weight(k)); k = i_parent(k); step_counter++; // only for measuring algorithm performance } } int get_heap_size(heap h){ return h->size; } void set_node_data(node n, void* data){ n->data = data; } void* get_node_data(node n){ return n->data; } int get_node_weight(node n){ return n->weight; }