C语言 哈夫曼树代码实现

  众所周知,哈夫曼树有两种构造方法,一种是左小右大(优先队列,左子树优先选权值小的,相等则选复杂度小的,相等则选队列位置靠前的),一种是先左后右(普通队列,取两个权值最小的,队列靠前的那个作为左子树,队列靠后的作为右子树,构造完后放队列末尾)。

 下面那个队列里先选C3,再选C8,所以C3是左子树。

下面代码展示左小右大的构造方法。

思路:

1.统计词频,选择排序(升序)

2.用上面的链表元素构造成子树,初始化权优先列表

3.对权优先列表进行合并剩下的一个元素就是哈夫曼树。

4.初始化栈,非递归先序遍历哈夫曼树。

5.遍历过程中先添加编码,再计算WPL。

非递归先序遍历
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define maxsize 50
typedef struct node {
    char data;
    int count;//词频
    struct node* next;
}node, * pnode;
typedef struct tree {//树
    int weight;//权值
    int complex;//复杂度
    char code[maxsize];
    pnode elem;
    struct tree* leftChild;
    struct tree* rightChild;
}tree, * pTree;
typedef struct List2 {//用来存子树
    struct tree* subtree;
    struct List2* next;//指向下一个
}List, * pList;
typedef struct stack {
    int top;
    pTree stelem[maxsize];
}Stack, *pStack;
int WPL;
pnode createList(char* q, int size);
void mergeList(pnode* m);//合并
void ptList(pnode head);//打印
void sortList(pnode* m);//选择排序

pList createPrlist(pnode p);
pList createnList(pList a, pList b);//创建没有elem结点,连接左右子树
void Prlistconnect(pList** m);//连接并按一定规则放列表中
pTree createHfmtree(pList* m);//创建哈夫曼树

pStack initStack();//初始化栈
int isEmpty(pStack stack);
int isFull(pStack stack);
int Push(pStack *s,pTree a);//入栈
int Pop(pStack *s,pTree* a);//出栈

void preorder(pTree* t);//先序遍历
int main() {
    int i, j, k, m,size;
    pnode head;
    char a[maxsize] = { 0 };
    WPL = 0;
    printf("请输入你的名字(拼音形式): ");
    scanf("%s", a);
    size = strlen(a);//数组大小
    head = createList(a, size);//尾插法
    mergeList(&head);
    sortList(&head);
    printf("词频统计: ");
    ptList(head);
    printf("\n");
    pList l= createPrlist(head);
    pTree root =createHfmtree(&l);
    
    printf("编码如下: \n");
    preorder(&root);
    printf("WPL=%d", WPL);

    return 0;
}
pnode createList(char* q, int size) {
    int i, j, k;
    pnode head, p, m = NULL;
    head = (pnode)malloc(sizeof(node));
    if (head == NULL) { return NULL; }
    head->next = NULL;
    for (i = 0; i < size; i++) {//尾插法

        p = (pnode)malloc(sizeof(node));
        if (p == NULL) { return NULL; }
        p->next = NULL;
        p->data = q[i];
        p->count = 1;
        if (head->next == NULL) {
            m = head->next = p;
        }
        else {
            m->next = p;
            m = m->next;
        }

    }
    return head;
}
void mergeList(pnode* m) {//合并同类
    int i, j;
    pnode p, q, s = NULL;
    p = q = (*m)->next;
    if (q == NULL) {
        printf("空链表");
        return;
    }
    while (q != NULL && q->next != NULL) {//合并
        while (p != NULL && p->next != NULL) {
            if (p->next->data == q->data) {
                s = p->next;
                q->count++;
                p->next = s->next;//p可能跳到NULL上
                free(s);
            }
            if (p->next != NULL && p->next->data != q->data) {//不相等才跳
                p = p->next;
            }
        }
        q = q->next;
        p = q;//复位
    }
}
void ptList(pnode head) {
    pnode p = head->next;
    while (p != NULL) {
        printf("%c %d ", p->data, p->count);
        p = p->next;
    }
}
void sortList(pnode* m) {//选择排序,升序
    int i, j;
    pnode p, q = NULL, s = NULL, t = NULL;
    q = *m;//q记录位置,方便p回滚
    p = (*m)->next;
    if (p == NULL) {
        printf("空链表");
        return;
    }
    while (q != NULL && q->next != NULL) {

        t = p = q->next;//p指向q
        while (p != NULL && p->next != NULL) {//选择排序,每次最小值放q后面
            if (p->next->count < t->next->count) {
                t = p;//方便操作t->next
            }
            p = p->next;
        }
        if (t->next != NULL && t->next->count < q->next->count) {//考虑输入为abbbb情况
            if (t->next->next != NULL) {
                s = t->next;
                t->next = s->next;
                s->next = q->next;//先连后面
                q->next = s;//再连前面
                q = q->next;
            }
            else {//t为倒数第二个元素
                t->next->next = q->next;
                q->next = t->next;
                t->next = NULL;
                q = q->next;
            }
        }
        else {//t为尾元素
            q = q->next;
        }

    }
}

pList createPrlist(pnode p) {//全部先放进带头结点权优先列表中
    pnode q;
    pTree tp;
    pList m,n,s=NULL;
    m= (pList)malloc(sizeof(List));
    q = p->next;
    if (q == NULL || m == NULL) { return NULL; }
    m->next = NULL;
    s = m;//s用来遍历
    while (q != NULL) {
        tp = (pTree)malloc(sizeof(tree));
        n = (pList)malloc(sizeof(List));
        if (tp == NULL||n==NULL ) { return NULL; }
        tp->elem = q;
        tp->code[0] = '\0';
        tp->weight = tp->elem->count;//权值
        tp->complex = 1;//复杂度
        tp->leftChild = tp->rightChild = NULL;

        n->subtree = tp;
        n->next = s->next;
        s->next = n;
        s = s->next;
        q = q->next;
    }
    
    return m;
}
pList createnList(pList a,pList b) {//创建不带pnode的列表元素
   pList tp;
   if (b == NULL) { return a; }//这行可以删
    tp = (pList)malloc(sizeof(List));
    if (tp == NULL) { return NULL; }
    tp->next = NULL;
    tp->subtree = (pTree)malloc(sizeof(tree));
    if (tp->subtree == NULL) { return NULL; }
    tp->subtree->elem = NULL;
    tp->subtree->code[0] = '\0';
    tp->subtree->leftChild=a->subtree;
    tp->subtree->rightChild=b->subtree;
    tp->subtree->weight = tp->subtree->leftChild->weight+ tp->subtree->rightChild->weight;//权值
    tp->subtree->complex = 1+ tp->subtree->leftChild->complex+ tp->subtree->rightChild->complex;//复杂度
    
    return tp;
}
void Prlistconnect(pList** m) {//连接后按一定规则放列表中
    pList p, q;
    p = (**m)->next;//改成三级指针
    
    q = createnList(p,p->next);
    if (q == NULL) { return ; }
    (**m)->next = p->next->next;
    //放回列表
    p = **m;
    while (p != NULL && p->next != NULL) {
        if (p->next->subtree->weight > q->subtree->weight) {
            q->next = p->next;
            p->next = q;
            return;
        }
        else if (p->next->subtree->weight < q->subtree->weight) {
            p = p->next;
        }
        else {//判断复杂度
            if (p->next->subtree->complex <= q->subtree->complex) {//复杂度相等时在相同权的尾部插入
                p = p->next;
            }
            else {
                q->next = p->next;
                p->next = q;
                return;
            }
        }
    }
    if (p->next == NULL ) {//最后一个子树放回去
        q->next = p->next;
        p->next = q;
        
    }
    

}

pTree createHfmtree(pList *m) {
    pList p, q,s,t;
    if ((*m) == NULL || (*m)->next == NULL) { return NULL; }
    p = (*m)->next;
    while (p != NULL && p->next != NULL) {
        Prlistconnect(&m);
        p = (*m)->next;
    }
    return (*m)->next->subtree;
}
pStack initStack() {
    pStack s = (pStack)malloc(sizeof(Stack));
    s->top = -1;
    s->stelem[0] = NULL;
    return s;
}
int isEmpty(pStack stack) {
    if (stack->top == -1) {
        return 1;
    }
    else {
        return 0;
    }
}
int isFull(pStack stack) {
    if (stack->top == maxsize - 1) {
        return 1;
    }
    else {
        return 0;
    }
}
int Push(pStack* s, pTree a) {

    if (isFull(*s)) {
        return -1;//异常返回-1
    }
    else {
        (*s)->stelem[++((*s)->top)] = a;
        return 1;
    }  
}
int Pop(pStack* s, pTree* a) {
    if (isEmpty(*s)) {
        return -1;
    }
    else {
        *a = (*s)->stelem[((*s)->top)--];
        return 1;
    }
}
void preorder(pTree* t) {
    int h=0;//深度
    pStack s = initStack();
    pTree p = (*t);
    while (p != NULL||s->top!=-1) {
        while (p != NULL) {
            //访问根节点
            if (p->elem != NULL) {
                printf("%c \n", p->elem->data);
                
                
                    
                
            }
            
            Push(&s, p);
            if ( p->leftChild != NULL) {
                strcat(p->leftChild->code, p->code);
                strcat(p->leftChild->code, "0");
                if (p->leftChild->elem != NULL) {
                    printf("%s ", p->leftChild->code);
                    WPL = WPL + strlen(p->leftChild->code)*(p->leftChild->elem->count);
                }
                
            }
            p = p->leftChild;
            
            
            //遍历左子树
            
        }
        //遍历右子树
        if (s->top != -1) {
            
            Pop(&s, &p);//pop会导致top改变,遍历右子树计算wpl不能用top
            if (p->rightChild != NULL) {
                
                strcat(p->rightChild->code, p->code);
                strcat(p->rightChild->code, "1");
                if (p->rightChild->elem != NULL) {
                    printf("%s ", p->rightChild->code);
                    WPL = WPL + strlen(p->rightChild->code) * (p->rightChild->elem->count);//用编码长度当成深度
                }
                
            }
            p = p->rightChild;
            
        }
    }
}
//计算wpl

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值