• 优先队列 二叉堆 Implementing a generic Priority Queue in C (using heaps)


    https://www.andreinc.net/2011/06/01/implementing-a-generic-priority-queue-in-c

    https://github.com/tylov/STC/blob/master/docs/cpque_api.md

    https://github.com/tylov/STC/blob/master/examples/priority.c

     二叉堆

    https://github.com/nomemory/c-generic-pqueue

    Implementing a generic Priority Queue in C (using heaps)

    June 1, 2011

    In Computer Science, a Priority Queue is an abstract data type, very similar to a normal Queue, but with a twist: every element has a priority attached to itself. The rule is that an element with high priority is served before an element with low priority.

    Our implementation is going to use a Binary Heap. This is fancy name for a heap structure that takes the form of a binary tree that is complete, and the key that is stored in each node is (>=) bigger than keys stored in its children.

    If the keys of the parent node are (<=) than the keys of the children are called: min-heaps.

    ImplementationPermalink

    The code is available on github.

    git clone https://github.com/nomemory/c-generic-pqueue.git
    

    Afer cloning, to compile the project simply:

    gcc -Wall main.c pqueue.c
    

    If you want to understand more about writing generic code in C, please reffer to my previous article: Generic Data Structures in C.

    Defining the dataPermalink

    We start by defining some “helpful” macros:

    // pqueue.h
    
    //
    // Debugging macro .
    //
    // Checks for a NULL pointer, and prints the error message, source file and
    // line via 'stderr' .
    // If the check fails the program exits with error code (-1) .
    //
    #define NP_CHECK(ptr)                                                           \
        {                                                                           \
            if (NULL == (ptr)) {                                                    \
                fprintf(stderr, "%s:%d NULL POINTER: %s n",                         \
                    __FILE__, __LINE__, #ptr);                                      \
                exit(-1);                                                           \
            }                                                                       \
        }                                                                           \
    
    #define DEBUG(msg) fprintf(stderr, "%s:%d %s", __FILE__, __LINE__, (msg))
    

    NP_CHECK will help us check if the memory was allocated correctly. If the returning pointer ptr is NULL, it aborts the program.

    The PQueue struct and the API looks like this:

    // pqueue.h
    
    //
    // Priority Queue Structure
    //
    typedef struct PQueue_s {
        /* The actual size of heap at a certain time */
        size_t size;
        /* The amount of allocated memory for the heap */
        size_t capacity;
        /* An array of (void*), the actual max-heap */
        void **data;
        /* A pointer to a comparator function, used to prioritize elements */
        int (*cmp)(const void *d1, const void *d2);
    } PQueue;
    
    // Allocates memory for a new Priority Queue .
    // Needs a pointer to a comparator function, thus establishing priorities .
    PQueue *pqueue_new(int (*cmp)(const void *d1, const void *d2),
                       size_t capacity);
    
    // De-allocates memory for a given Priority Queue 
    void pqueue_delete(PQueue *q);
    
    // Add an element inside the Priority Queue 
    void pqueue_enqueue(PQueue *q, const void *data);
    
    // Removes the element with the greatest priority from within the Queue 
    void *pqueue_dequeue(PQueue *q);
    

    You might be unfamiliar with the int (*cmp)(const void *d1, const void *d2). That’s actually a pointer to a function that compares two values. In more modern languages, this is a Comparator method.

    When we are going to create the PQueue it will work like this:

    // A method that compares two ints (referenced by their pointers)
    int cmp_ints(const void *int1, const void *int2) {
        return *(int*) int1 - *(int*) int2;
    }
    
    // ...
    
    PQueue* pq = pqueue_new(cmp_ints, 200);
    
    //...
    

    Implementing the constructor-like/destructor-like methods:

    /**
    * Allocates memory for a new Priority Queue structure .
    
    * 'cmp' function:
    *   returns 0 if d1 and d2 have the same priorities
    *   returns [negative value] if d1 have a smaller priority than d2
    *   returns [positive value] if d1 have a greater priority than d2
    */
    PQueue *pqueue_new(int (*cmp)(const void *d1, const void *d2),
                       size_t capacity) {
        PQueue *res = NULL;
        NP_CHECK(cmp);
        res = malloc(sizeof(*res));
        NP_CHECK(res);
        res->cmp = cmp;
        /* The inner representation of data inside the queue is an array of void* */
        res->data = malloc(capacity * sizeof(*(res->data)));
        NP_CHECK(res->data);
        res->size = 0;
        res->capacity = capacity;
        return (res);
    }
    
    /**
    * De-allocates memory for a given Priority Queue structure .
    */
    void pqueue_delete(PQueue *q) {
        if (NULL == q) {
            DEBUG("Priority Queue is already NULL. Nothing to free.");
            return;
        }
        free(q->data);
        free(q);
    }
    

    Implementing the rest of the API:

    /**
    * Adds a new element to the Priority Queue .
    */
    void pqueue_enqueue(PQueue *q, const void *data) {
        size_t i;
        void *tmp = NULL;
        NP_CHECK(q);
        if (q->size >= q->capacity) {
            DEBUG("Priority Queue is full. Cannot add another element .");
            return;
        }
        /* Adds element last */
        q->data[q->size] = (void*) data;
        i = q->size;
        q->size++;
        /* The new element is swapped with its parent as long as its
        precedence is higher */
        while(i > 0 && q->cmp(q->data[i], q->data[PARENT(i)]) > 0) {
            tmp = q->data[i];
            q->data[i] = q->data[PARENT(i)];
            q->data[PARENT(i)] = tmp;
            i = PARENT(i);
        }
    }
    
    /**
    * Returns the element with the biggest priority from the queue .
    */
    void *pqueue_dequeue(PQueue *q) {
        void *data = NULL;
        NP_CHECK(q);
        if (q->size < 1) {         
             /* Priority Queue is empty */         
             DEBUG("Priority Queue underflow . Cannot remove another element .");         
             return NULL;     
        }     
        data = q->data[0];
        q->data[0] = q->data[q->size-1];
        q->size--;
        /* Restore heap property */
        pqueue_heapify(q, 0);
        return (data);
    }
    
    /**
    * Turn an "almost-heap" into a heap .
    */
    void pqueue_heapify(PQueue *q, size_t idx) {
        /* left index, right index, largest */
        void *tmp = NULL;
        size_t l_idx, r_idx, lrg_idx;
        NP_CHECK(q);
    
        l_idx = LEFT(idx);
        r_idx = RIGHT(idx);
    
        /* Left child exists, compare left child with its parent */
        if (l_idx < q->size && q->cmp(q->data[l_idx], q->data[idx]) > 0) {
            lrg_idx = l_idx;
        } else {
            lrg_idx = idx;
        }
    
        /* Right child exists, compare right child with the largest element */
        if (r_idx < q->size && q->cmp(q->data[r_idx], q->data[lrg_idx]) > 0) {
            lrg_idx = r_idx;
        }
    
        /* At this point largest element was determined */
        if (lrg_idx != idx) {
            /* Swap between the index at the largest element */
            tmp = q->data[lrg_idx];
            q->data[lrg_idx] = q->data[idx];
            q->data[idx] = tmp;
            /* Heapify again */
            pqueue_heapify(q, lrg_idx);
        }
    }
    

    To test the code works accordingly:

    #include <stdio.h>
    #include <stdlib.h>
    
    #include "pqueue.h"
    
    int cmp_ints(const void *int1, const void *int2) {
        return *(int*) int1 - *(int*) int2;
    }
    
    int main(int argc, char** argv) {
        
        PQueue* pq = pqueue_new(cmp_ints, 200);
        
        int x = 100, y = 50, z = 300, k = 100, w = 1000;
        
        pqueue_enqueue(pq, &x);
        pqueue_enqueue(pq, &y);
        pqueue_enqueue(pq, &z);
        pqueue_enqueue(pq, &k);
        pqueue_enqueue(pq, &w);
        
        int i = 0;
        for(;i<5;++i)
            printf("%d\n", *(int*) pqueue_dequeue(pq));
        
        pqueue_delete(pq);
        
        return (EXIT_SUCCESS);
    } 
    

    And the output is:

    1000
    300
    100
    100
    50
    

    Updated: June 1, 2011

     

     

     

    https://github.com/tylov/STC/blob/master/docs/cpque_api.md

    STC cpque: Priority Queue

    A priority queue is a container adaptor that provides constant time lookup of the largest (by default) element, at the expense of logarithmic insertion and extraction. A user-provided i_cmp may be defined to set the ordering, e.g. using -c_default_cmp would cause the smallest element to appear as the top() value.

    See the c++ class std::priority_queue for a functional reference.

    Header file and declaration

    #define i_val       // value: REQUIRED
    #define i_cmp       // three-way compare two i_val* : REQUIRED IF i_val/i_valraw is a non-integral type
    #define i_valdrop   // destroy value func - defaults to empty destruct
    #define i_valclone  // REQUIRED IF i_valdrop defined
    #define i_valraw    // convertion type
    #define i_valfrom   // convertion func i_valraw => i_val
    #define i_valto     // convertion func i_val* => i_valraw.
    #define i_tag       // defaults to i_val
    #define i_type      // container type name
    #include <stc/cpque.h>

    X should be replaced by the value of i_tag in all of the following documentation.

    Methods

    cpque_X             cpque_X_init(void);                    // create empty pri-queue.
    cpque_X             cpque_X_with_capacity(size_t cap);
    cpque_X             cpque_X_with_size(size_t size, i_val null);
    cpque_X             cpque_X_clone(cpque_X pq);
    
    void                cpque_X_clear(cpque_X* self);
    bool                cpque_X_reserve(cpque_X* self, size_t n);
    void                cpque_X_shrink_to_fit(cpque_X* self);
    void                cpque_X_copy(cpque_X* self, cpque_X other);
    void                cpque_X_drop(cpque_X* self);        // destructor
    
    size_t              cpque_X_size(cpque_X pq);
    bool                cpque_X_empty(cpque_X pq);
    i_val*              cpque_X_top(const cpque_X* self);
    
    void                cpque_X_make_heap(cpque_X* self);  // heapify the vector.
    void                cpque_X_push(cpque_X* self, i_val value);
    void                cpque_X_emplace(cpque_X* self, i_valraw raw); // converts from raw
    
    void                cpque_X_pop(cpque_X* self);
    void                cpque_X_erase_at(cpque_X* self, size_t idx);
    
    i_val               cpque_X_value_clone(i_val value);

    Types

    Type nameType definitionUsed to represent...
    cpque_X struct {cpque_X_value* data; ...} The cpque type
    cpque_X_value i_val The cpque element type

    Example

    #include <stc/crandom.h>
    #include <stdio.h>
    
    #define i_val int64_t
    #define i_cmp -c_default_cmp // min-heap
    #define i_tag i
    #include <stc/cpque.h>
    
    int main()
    {
        size_t N = 10000000;
        stc64_t rng = stc64_new(1234);
        stc64_uniform_t dist = stc64_uniform_new(0, N * 10);
    
        // Declare heap, with defered drop()
        c_auto (cpque_i, heap)
        {
            // Push ten million random numbers to priority queue, plus some negative ones.
            c_forrange (N)
                cpque_i_push(&heap, stc64_uniform(&rng, &dist));
            c_apply(v, cpque_i_push(&heap, v), int, {-231, -32, -873, -4, -343});
    
            // Extract and display the fifty smallest.
            c_forrange (50) {
                printf("%" PRIdMAX " ", *cpque_i_top(&heap));
                cpque_i_pop(&heap);
            }
        }
    }
     

    Output:

     -873 -343 -231 -32 -4 3 5 6 18 23 31 54 68 87 99 105 107 125 128 147 150 155 167 178 181 188 213 216 272 284 287 302 306 311 313 326 329 331 344 348 363 367 374 385 396 399 401 407 412 477
    

     

     

  • 相关阅读:
    Linux系统安装
    设计模式的原则
    vue基础
    软考常考题目及解题技巧
    软件设计师
    Wireshark 使用教程
    JVM 调优
    Shell脚本编写
    Linux相关知识
    HTTP缓存机制及原理
  • 原文地址:https://www.cnblogs.com/sinferwu/p/16295271.html
Copyright © 2020-2023  润新知