众所周知,哈夫曼树有两种构造方法,一种是左小右大(优先队列,左子树优先选权值小的,相等则选复杂度小的,相等则选队列位置靠前的),一种是先左后右(普通队列,取两个权值最小的,队列靠前的那个作为左子树,队列靠后的作为右子树,构造完后放队列末尾)。
下面那个队列里先选C3,再选C8,所以C3是左子树。
下面代码展示左小右大的构造方法。
思路:
1.统计词频,选择排序(升序)
2.用上面的链表元素构造成子树,初始化权优先列表
3.对权优先列表进行合并剩下的一个元素就是哈夫曼树。
4.初始化栈,非递归先序遍历哈夫曼树。
5.遍历过程中先添加编码,再计算WPL。
![](https://img-blog.csdnimg.cn/31efac60825040de8ae5604e912ee3a0.png)
#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