#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#ifndef MACRO_//宏
#define MACRO_
#define OK 1
#define FALSE 0
#define ERROR 0
#define TURE 1
#define OVERFLOW -2
#define INFEASIBLE -1
typedef int Status;
typedef int BOOL;
#endif
/*注意,自顶向下求编码会破坏HT的原本数据,都变成2,要想克服,需采用自底向上求编码
或者另设记录每个元素访问记录的数组
同时,也无法正确解码
-----------------严蔚敏教材缺陷---------------
*/
typedef struct
{
int weight;
int parent, lchild, rchild;
}HTNode, *HuffmanTree;//动态分配数组存储赫夫曼树
typedef char **HuffmanCode;//动态分配数组存储赫夫曼编码表
Status Input(int *ElemNum, int **WeightArray);
//输入权值
//成功返回OK,ElemNum不合适返回ERROR
void SlectMin(int i, int *S1, int *S2, HuffmanTree HT);
//从HuffmanTree中选出Parent为0,权值最小的两个数,分别将下标存放在S1和S2中,其中S1中存放的值<S2中存放的值
//i表示当前有效结点的下标
void CreatHuffmanTree(HuffmanTree *HT, int *WeightArray, int ElemNum);
//WeightArray存放ElemNum个字符的权值(均>0),构造赫夫曼树HT
void HuffmanCoding_FromRoot(HuffmanTree HT, HuffmanCode *HC, int ElemNum);
//从头结点开始遍历,求出n个字符的赫夫曼编码HC
void Decode(HuffmanTree HT, int ElemNum);
//输入哈夫曼编码,解码
int main()
{
int ElemNum, *WeightArray;
HuffmanTree HT;
HuffmanCode HC;
while (Input(&ElemNum, &WeightArray) == ERROR)
printf("The value of ElemNum is not feasible ! \n");
CreatHuffmanTree(&HT, WeightArray, ElemNum);
HuffmanCoding_FromRoot(HT, &HC, ElemNum);
for (int i = 1; i <= ElemNum; ++i)
printf("%-3d ,coding is : %s\n", HT[i].weight, HC[i]);
/*注意,自顶向下求编码会破坏HT的原本数据,都变成2,要想克服,需采用自底向上求编码
或者另设记录每个元素访问记录的数组
-----------------严蔚敏教材缺陷---------------
*/
Decode(HT, ElemNum);
system("pause");
}
Status Input(int *ElemNum,int **WeightArray)
//输入权值
//成功返回OK,ElemNum不合适返回ERROR
{
printf("How many elems ? \n");
scanf("%d", ElemNum);
if (*ElemNum>1)//小于2还建什么Huffman树呢?
{
*WeightArray = (int *)malloc(sizeof(int)*(*ElemNum));
if (!(*WeightArray))
exit(OVERFLOW);
printf("\nPlease Enter the weight\n");
for (int i = 0; i < (*ElemNum); ++i)
scanf("%d", &(*WeightArray)[i]);
return OK;
}//if
return ERROR;
}//CrearHuffmanTree
void CreatHuffmanTree(HuffmanTree *HT,int *WeightArray,int ElemNum)
//WeightArray存放ElemNum个字符的权值(均>0),构造赫夫曼树HT
{
for (int i = 0; i < ElemNum; ++i)
printf("%d ", WeightArray[i]);
int TotalNum = 2 * ElemNum - 1;//总结点数
*HT = (HuffmanTree)malloc(sizeof(HTNode)*(TotalNum + 1));//0号单元弃用,动态分配数组存储赫夫曼树
if (!*HT)exit(OVERFLOW);//分配失败
HuffmanTree p = *HT;
int i,S1,S2;
for (i = 1; i <=ElemNum; ++i)
(*HT)[i] = {WeightArray[i-1], 0, 0, 0 };//初始化赫夫曼树前ElemNum个结点
for (i; i <= TotalNum; ++i)
(*HT)[i] = { 0, 0, 0, 0 };//初始化后面的结点
for (i = 0; i < ElemNum-1; ++i)//ElemNum需循环建树ElemNum-1次
{
SlectMin(i + ElemNum, &S1, &S2, *HT);//选出两个最小值S1,S2,其中S1的值小于S2的值
//printf("\nTake %d %d ", S1, S2);
(*HT)[S1].parent = (*HT)[S2].parent = i + ElemNum + 1;//i+ElemNum+1的值是父节点的下标
(*HT)[i + ElemNum + 1].lchild = S1;
(*HT)[i + ElemNum + 1].rchild = S2;
(*HT)[i + ElemNum + 1].weight = (*HT)[S1].weight + (*HT)[S2].weight;
}//for
printf("\n---------------------Creat HuffmanTree OK !---------------------\n");
}//CreatHuffmanTree
void SlectMin(int i,int *S1,int *S2,HuffmanTree HT)
//从HuffmanTree中选出Parent为0,权值最小的两个数,分别将下标存放在S1和S2中,其中S1中存放的值<S2中存放的值
//i表示当前有效结点的下标
{
int j;
*S1 = *S2 = INT_MAX;
for (j = 1; j <= i; ++j)//选出第一个最小值
{
if (HT[j].parent != 0)//父节点需为0
continue;
if (HT[*S1].weight > HT[j].weight)
*S1 = j;
}//for
for (j = 1; j <= i; ++j)//选出第二个最小值
{
if (HT[j].parent != 0)//父节点需为0
continue;
if (j == *S1)//不能选刚选过的
continue;
if (HT[*S2].weight > HT[j].weight)
*S2 = j;
}//for
}//SlectMin
void HuffmanCoding_FromRoot(HuffmanTree HT,HuffmanCode *HC,int ElemNum)
//从头结点开始遍历,求出n个字符的赫夫曼编码HC
{
*HC = (HuffmanCode)malloc(sizeof(char *)*(ElemNum + 1));
if (!(*HC))exit(OVERFLOW);
char *buffer = (char *)malloc(sizeof(char)*ElemNum);//分配编码缓冲区大小
if (!buffer)exit(OVERFLOW);
int CodeNum = 2 * ElemNum - 1;//正在访问结点的下标
int CodeLen = 0;//编码长度
for (int i = 1; i <= CodeNum; ++i) HT[i].weight = 0;//遍历赫夫曼树时用作结点状态标志(相当于初始化访问次数)
while (CodeNum)//访问到根结点的父节点即0,说明遍历完该树
{
if (HT[CodeNum].weight == 0)//还没被访问过
{
HT[CodeNum].weight = 1;//标记访问一次
if (HT[CodeNum].lchild != 0)//有左子树,访问它的左子树
{
CodeNum = HT[CodeNum].lchild;
buffer[CodeLen++] = '0';//向左访问,编码为0
}//if
else if (HT[CodeNum].rchild == 0)//左子树右子树都为0,说明它是叶子结点
{
buffer[CodeLen] = '\0';
(*HC)[CodeNum] = (char *)malloc(sizeof(char)*(CodeLen + 1));
if (!(*HC)[CodeNum])exit(OVERFLOW);//分配存储空间失败
strcpy((*HC)[CodeNum], buffer);//将编码拷贝
}//else if
}//if
else if (HT[CodeNum].weight==1)//被访问过一次,即该结点左子树被访问过,接下来访问它的右子树
{
HT[CodeNum].weight = 2;//标记访问2次
if (HT[CodeNum].rchild != 0)//有右子树,访问它的右子树
{
CodeNum = HT[CodeNum].rchild;
buffer[CodeLen++] = '1';//向右访问,编码为1
}//if
}//else if
else//被访问过2次,即左子树右子树都被访问过,回溯
{
--CodeLen;
CodeNum = HT[CodeNum].parent;
}//else
}//while
}//HuffmanCoding
void Decode(HuffmanTree HT,int ElemNum)
//输入哈夫曼编码,解码
{
int i = 2*ElemNum-1;
char buffer[20];
fflush(stdin);
printf("Enter the huffmanCode : ");
gets(buffer);
int j = 0;
while (buffer[j]!='\0')
{
if (buffer[j]=='0')
i = HT[i].lchild;
else i = HT[i].rchild;
++j;
}//while
printf("The num is %d", HT[i].weight);
}//Decode