Generic binary search tree in C

血红的双手。 提交于 2019-12-01 08:21:45
AusCBloke

You're going to need to create a comparison function for each data type that's used and pass a function pointer to each function that needs to know if two pieces of data are equal or greater/less than each other. Only this function will have to know the internal data type.

This function would look like:

int compare_X(const void *d1, const void *d2)

And the function should return 0 if the two objects are equal, less than 0 if the object pointed to by d1 is smaller, or greater than 0 otherwise. You would have a range of these functions, such as compare_int, compare_double, etc, depending on the type of data you're storing in a specific tree.


You would then add this argument to the functions the need to compare two objects:

int (*cpm_fptr)(const void *, const void *)


Now for example in Insert, if(data > (node->data)) would become:

if (cmp_fptr(data, node->data) > 0) /* data > node->data */

Also:

if (cmp_fptr(data, node->data) == 0) /* data == node->data */

if (cmp_fptr(data, node->data) < 0) /* data < node->data */


The signature of Insert would now look like:

treeNode * Insert(treeNode *node, int data, 
                  int (*cpm_fptr)(const void *, const void *))

And if your internal type was int, you might call it like:

Insert(node, my_int, compare_int);


This is how functions like bsearch and qsort are able to operate on data of any type.

You may use a union to represent the data you want to store, along with information of the type the union represents at any time. Something like below:

typedef struct _generic_data {
    union {
        int         i;              /* Integer */
        long        l;              /* Long */
        float       f;              /* floating point */
        double      d;              /* double precision floating point */
        char        c;              /* char */
        char        *s;             /* c string */
        struct {
            void        *blob;      /* Arbitrary blog of binary data */
            int         size;       /* Size of this blob */
        }           b;              /* You may not really need it
                                     * So you can get rid of this struct
                                     * if you want.
                                     */
    } value;                        /* To access the above values */
    int type_id;                    /* To identify which data type is actually 
                                     * being stored in this generic data struct
                                     */
} generic_data;

Of course you should also have the corresponding unsigned types for the above types too for completeness sake. Set type_id to distinctly identify elements. For example:

const int char_type_id = 1;
const int long_type_id = 2;
....
const int blob_type_id = 10;
const int error_type_id = -42;

and so on, so that the following holds for generic_data gd;

  • When gd.type_id == char_type_id, then gd.value.c is the valid value.
  • Same for the rest of the types.

So now, your Node would look like:

typedef struct treeNode {
  generic_data*   data;
  struct treeNode *left;
  struct treeNode *right;
} treeNode;

You would need to modify your functions as

treeNode * Insert(treeNode *node, generic_data* data);
treeNode * Delete(treeNode *node, generic_data* data);

You would also need a function which is able to compare between two generic_data values. Something like this:

long compare_generic(generic_data* lhs, generic_data* rhs) {
    if ( lhs == NULL || rhs == NULL ) {
        return error_type_id;
    }
    if ( lhs->type_id != rhs->type_id ) {
        /*
         * ERROR: Trying to compare two different types.
         * Do appropriate error handling here.
         * return some eror code.
         */
        return error_type_id;
    }
    switch( lhs->type_id ) {
        case char_type_id: return (long)(lhs->value.c - rhs.value.c); break;
        case int_type_id:  return (long)(lhs->value.i - rhs.value.i); break;
        /*
         * Something similarly logical for long, float, double.
         * The basic idea if this function returns 0 
         *
         * void *blob allows you to store arbitrary binary data. You 
         * may not need it, but if you do, there should be some way to
         * compare between the two.
         */
        default:
            /*
             * No type_id matches.
             * Handle this error case.
             * return some error code.
             */
            return error_type_id;
            break; /* Just a habbit to always have a break so that
                    * you don't have to deal with special cases.
                    */
    }
}

This would be used to replace your existing code as below:

  • if(data < node->data) to this : if ( compare_generic( data, node->data ) < 0 )
  • if(data > node->data) to this : if ( compare_generic( data, node->data ) > 0 )
  • if(data == node->data) to this : if ( compare_generic( data, node->data ) == 0 )

You would now have to be extra careful in accessing your values.

If you really want this to be in C, you'll need a little more sophisticated approach (storing the type of the data in the tree in a variable and performing type-casts wherever necessary).

However, if you decide to do the same in C++, you can use Templates. There are numerous examples on Templates available on the web.

Hope this helps!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!