从递归霍夫曼树C返回结构数组

时间:2017-06-28 10:16:37

标签: c

我在课堂上有一个任务,从霍夫曼树中返回一个被击中的符号数组。 函数getSL得到一个霍夫曼树(仅)并返回符号的敲击。 数组中的每个点都包含来自树的“叶子”的字符和 他的代码长度(到叶子的横截面数)。 我的主要问题是找到我如何推进arry的cnt,它不会直截了当。 谢谢。

typedef  struct  HNode {
    char  chr;
    struct HNode *left, *right;
} HNode;

typedef  struct {
    char chr;
    int counter;
}Symbol;

这就是我现在所做的。

Symbol * getSL(HNode *root) {

    if (root->left == NULL && root->right == NULL) {
        Symbol* b = (Symbol*)malloc(100);
        b->counter=0;
        b->chr = root->chr;
        return b;
    }
    Symbol* a = (Symbol*)malloc(100);

    if (root->left != NULL) {
        a= getSL(root->left);
        a->counter++;
    }

    if (root->right != NULL) {
        a= getSL(root->right);
        a->counter++;
    }
    return a;
}

1 个答案:

答案 0 :(得分:0)

除了malloc问题(参见评论),你有一个基本的问题:你分配一个新的结构,然后替换它与递归调用返回的结构。所以你失去了之前创建的那个(实际上,内存泄漏!)。

最简单的变体现在将您的符号转换为链接列表节点;然后你就可以做到:

Symbol* lastLeafFound; // probaly a function parameter!

if(!(root->left || root->right))
{
    // leaf found:
    Symbol* a = (Symbol*)malloc(sizeof(Symbol));
    a->chr = root->chr;
    a->counter = /* ... */;
    a->next = NULL;
    lastLeafFound->next = a;
    // you might return a now as last leaf found, using it in the next recursive call
}

当然,上面的代码不完整,但应该给你一个想法......

如果你不能修改你的结构,那么你需要创建一个数组并将其传递给每个新的递归调用(而不是使用全局变量):

void doGetSL
(
    HNode* root,
    Symbol** symbols,      // your array to be used
    unsigned int* count,   // number of symbols contained so far
    unsigned int* capacity // maximum possible symbols
)

将所有数据作为指针传递允许函数根据需要修改它们,它们仍可从外部获取...

Symbol* getSL(HNode* root)
{
    if(!root)
        return NULL;

    unsigned int count = 0;
    unsigned int capacity = 128;
    // allocate a whole array:
    Symbol* array = malloc(capacity*sizeof(Symbol));

    if(array) // malloc could fail...
    {
        doGetSL(root, &array, &count, &capacity);

        // as you cannot return the number of leaves together with
        // the array itself, you will need a sentinel:
        array[count].chr = 0;
        // obvious enough, I'd say, alternatively you could
        // set counter to 0 or -1 (or set both chr and counter) 
    }
    return array;
}

doGetSL现在将使用上面设置的“基础设施”:

{
    if(!(root->left || root->right))
    {
        if(*count == *capacity)
        {
            // no memory left -> we need a larger array!

            // store in separate variables:
            unsigned int c = *capacity * 2;
            Symbol* s = realloc(symbols, c * sizeof(Symbol));
            // now we can check, if reallocation was successful
            // (on failure, s will be NULL!!!):
            if(s)
            {
                // OK, we can use them...
                *symbols = s; // <- need a pointer for (pointer to pointer)!
                *capacity = c;
            }
            else
            {
                // re-allocation failed!
                // -> need appropriate error handling!
            }
        }

        (*symbols)[count].chr = root->chr;
        (*symbols)[count].counter = /*...*/;
        ++*count;

    }
    else
    {
        if(root->left)
        {
            doGetSL(root->left, symbols, count, capacity);
        }
        if(root->right)
        {
            doGetSL(root->right, symbols, count, capacity);
        }
    }
}

有一点遗漏:设置计数器。这将非常简单:向doGetSL添加另一个参数,指示当前深度,当您输入doGetSL时,您可以向右递增,然后您可以在需要时分配此值。

如果引入新结构,可以进一步改进上述变体(特别是可读性):

struct SLData
{
    Symbol* symbols,      // your array to be used
    unsigned int count,   // number of symbols contained so far
    unsigned int capacity // maximum possible symbols
};

并传递这一个而不是三个指针:

doGetSL(HNode*, struct SLData*, unsigned int depth);

struct SLData data = 
{
    .count = 0;
    .capacity = 128;
    .array = malloc(capacity*sizeof(Symbol));
};
if(data.array)
    doGetSL(root, &data, 0); // again passed as pointer!