C语言实现哈夫曼树求解及其编码输出

目标

给定一组权值,根据权值求其一个哈夫曼树,并通过中序遍历的顺序输出叶子节点的哈夫曼编码。

分析

首先回顾哈夫曼树的求解过程:

  1. 在权值中取最小的两个x,y,以这两个权值为叶子节点,生成一个权值为x+y的父亲节点;
  2. 从权值组中删除x,y,添加x+y进入权值组;
  3. 循环直到权值组仅剩一个元素,该元素为根节点的权值。

一个重要的点是我们希望输入的权值有序,并且时刻保持有序,因此选用链表来存储权重,并每次插入时都按序排序,接下来只要用代码实现上述步骤便可完成哈夫曼树的构建;

那么如何得到叶子节点的哈夫曼编码呢?

考虑使用递归为每个节点都分配一个编码,储存在树节点中。

代码实现

树节点和链表节点的定义与初始化

struct node
{
    int weight, idx;
    struct node *parent, *lchild, *rchild;
    char *code;
};

struct linknode
{
    int info;
    struct linknode *next;
    struct node *tree;
};
typedef struct linknode *Plink;
typedef struct node *Ptree;

Ptree createNULLtree()
{
    Ptree head = (Ptree)malloc(sizeof(struct node));
    if (head == NULL)
        printf("fail to create tree node!\n");
    head->parent = NULL;
    head->lchild = NULL;
    head->rchild = NULL;
    head->idx = 0;
    head->code = (char *)malloc(sizeof(char) * 10);
    return head;
}

Plink createNULLlink()
{
    Plink head = (Plink)malloc(sizeof(struct linknode));
    if (head == NULL)
        printf("fail to create link node!\n");
    head->next = NULL;
    head->tree = NULL;
    return head;
}

树节点中包含指向父亲节点和孩子节点的指针、权值、以及储存着编码的code和编码长度索引idx;

链表节点中包含权值大小info、指向下个节点的next指针以及指向权值对应树节点的tree指针。

一些工具函数

void insertwithorder(Plink head, int x, Ptree tree)
{
    Plink cur = head->next, last = head;
    if (tree == NULL)
        tree = createNULLtree();
    tree->weight = x;
    while (cur != NULL)
    {
        if (x <= cur->info)
        {
            last->next = createNULLlink();
            last->next->info = x;
            last->next->tree = tree;
            last->next->next = cur;
            return;
        }
        last = cur;
        cur = cur->next;
    }
    last->next = createNULLlink();
    last->next->info = x;
    last->next->tree = tree;
    last->next->next = cur;
}

将数据有序插入链表,小数据在前,插入和删除都比较方便

int is_over(Plink head)
{
    if (head->next->next == NULL)
        return 1;
    return 0;
}

判断是否应该结束程序,当链表中仅剩一个数据时结束

void LDR(Ptree head)
{
    if (head == NULL)
        return;
    LDR(head->lchild);
    visitleaf(head);
    LDR(head->rchild);
}

中序遍历

void visitleaf(Ptree head)
{
    if (head->lchild == NULL && head->rchild == NULL)
        printf("%d %s\n", head->weight, head->code);
}

只打印叶子节点的信息

生成二叉树

使用两个权值来创建一个二叉树,根节点权值为两权值之和;

实现过程中由于不仅仅需要链表节点的权值信息,所以没有单独使用函数实现这个这个功能;

大致流程如下:

  1. 获得两个权值及它们的树节点;
  2. 生成一个权值为其之和的树节点;
  3. 以新的树节点为父亲节点,连接这三个节点;
  4. 将求和之后的权值再按照顺序插入链表中;
  5. 使用递归,直到链表中仅剩一个元素;
  6. 至此,获得了哈夫曼树的根节点;
void createBiTree(Plink Link)
{
    if (is_over(Link))
        return;
    int nums[2]; //储存两个权值
    Plink temp = Link->next;
    Ptree p = createNULLtree(), lch, rch;
    nums[0] = temp->info;
    lch = temp->tree;
    Link->next = temp->next;
    free(temp);
    temp = Link->next;
    nums[1] = temp->info;
    rch = temp->tree;
    Link->next = temp->next;
    free(temp);
    p->weight = nums[0] + nums[1];
    p->lchild = lch;
    p->rchild = rch;
    lch->weight = nums[0];
    rch->weight = nums[1];
    lch->parent = p;
    rch->parent = p;
    insertwithorder(Link, p->weight, p);
    createBiTree(Link);
}

哈夫曼编码

void encode(Ptree head, char info)
{
    if (head == NULL)
        return;
    if (head->parent != NULL)
    {
        strcpy(head->code, head->parent->code);
        head->idx = strlen(head->code);
    }
    head->code[head->idx++] = info;
    head->code[head->idx] = '\0';
    encode(head->lchild, '0');
    encode(head->rchild, '1');
}

encode函数接收树节点和一个字符变量,左子树接收’0’,右子树接收‘1’,并且每个树节点会copy父亲节点的code信息,如此递归,便完成了编码。

主函数

int main()
{
    int num, temp;
    Plink linkhead = createNULLlink();
    scanf("%d", &num);
    for (int i = 0; i < num; i++)
    {
        scanf("%d", &temp);
        insertwithorder(linkhead, temp, NULL);
    }
    createBiTree(linkhead);
    Ptree treehead = linkhead->next->tree;
    if (treehead == NULL)
        printf("wrong!");
    encode(treehead, '\0');
    LDR(treehead);
    return 0;
}

至此便完成了题目要求。

全部代码

#include 
#include 
#include 

struct node
{
    int weight, idx;
    struct node *parent, *lchild, *rchild;
    char *code;
};

struct linknode
{
    int info;
    struct linknode *next;
    struct node *tree;
};
typedef struct linknode *Plink;
typedef struct node *Ptree;

Ptree createNULLtree()
{
    Ptree head = (Ptree)malloc(sizeof(struct node));
    if (head == NULL)
        printf("create tree wrongly!\n");
    head->parent = NULL;
    head->lchild = NULL;
    head->rchild = NULL;
    head->idx = 0;
    head->code = (char *)malloc(sizeof(char) * 10);
    return head;
}

Plink createNULLlink()
{
    Plink head = (Plink)malloc(sizeof(struct linknode));
    if (head == NULL)
        printf("create link wrongly!\n");
    head->next = NULL;
    return head;
}

void insertwithorder(Plink head, int x, Ptree tree)
{
    Plink cur = head->next, last = head;
    if (tree == NULL)
        tree = createNULLtree();
    tree->weight = x;
    while (cur != NULL)
    {
        if (x <= cur->info)
        {
            last->next = createNULLlink();
            last->next->info = x;
            last->next->tree = tree;
            last->next->next = cur;
            return;
        }
        last = cur;
        cur = cur->next;
    }
    last->next = createNULLlink();
    last->next->info = x;
    last->next->tree = tree;
    last->next->next = cur;
}

int is_over(Plink head)
{
    if (head->next->next == NULL)
        return 1;
    return 0;
}

void createBiTree(Plink Link)
{
    if (is_over(Link))
        return;
    int nums[2];
    Plink temp = Link->next;
    Ptree p = createNULLtree(), lch, rch;
    nums[0] = temp->info;
    lch = temp->tree;
    Link->next = temp->next;
    free(temp);
    temp = Link->next;
    nums[1] = temp->info;
    rch = temp->tree;
    Link->next = temp->next;
    free(temp);
    p->weight = nums[0] + nums[1];
    p->lchild = lch;
    p->rchild = rch;
    lch->weight = nums[0];
    rch->weight = nums[1];
    lch->parent = p;
    rch->parent = p;
    insertwithorder(Link, p->weight, p);
    createBiTree(Link);
}

void encode(Ptree head, char info)
{
    if (head == NULL)
        return;
    if (head->parent != NULL)
    {
        strcpy(head->code, head->parent->code);
        head->idx = strlen(head->code);
    }
    head->code[head->idx++] = info;
    head->code[head->idx] = '\0';
    encode(head->lchild, '0');
    encode(head->rchild, '1');
}

void visitleaf(Ptree head)
{
    if (head->lchild == NULL && head->rchild == NULL)
        printf("%d %s\n", head->weight, head->code);
}

void LDR(Ptree head)
{
    if (head == NULL)
        return;
    LDR(head->lchild);
    visitleaf(head);
    LDR(head->rchild);
}

int main()
{
    int num, temp;
    Plink linkhead = createNULLlink();
    scanf("%d", &num);
    for (int i = 0; i < num; i++)
    {
        scanf("%d", &temp);
        insertwithorder(linkhead, temp, NULL);
    }
    createBiTree(linkhead);
    Ptree treehead = linkhead->next->tree;
    if (treehead == NULL)
        printf("wrong!");
    encode(treehead, '\0');
    LDR(treehead);
    return 0;
}

你可能感兴趣的:(C,c语言,链表,数据结构)