版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30648823/article/details/79764699
// 哈夫曼树.cpp: 定义控制台应用程序的入口点。
// 陈鹏
#include "stdafx.h"
#include "stdio.h"
#include "malloc.h"
#define MAXSIZE 100
typedef struct shuZhu { //给定数组类型里面只可以存放hTree类型的指针
struct hTree *p;
}ShuZhu;
//这颗哈夫曼树没有数据类型 只有权值 如果要加入数据 那就在14行加入 char a; 写入数据的时候加入读取 a并保存 就行了
typedef struct hTree { //定义二叉树 weight为权值 left为左边指针 right为右边指针
int weight;
struct hTree *left, *right;
}HTree;
ShuZhu arr[MAXSIZE];
int t = 0;
//----------------------建立数据-----------------------------
//写入数据
void Init(int w, int t) { //w权值 d数据 t位置
HTree *p;
p = (HTree*)malloc(sizeof(hTree));
arr[t].p = p;
arr[t].p->weight = w;
//初始化
arr[t].p->left = NULL;
arr[t].p->right = NULL;
}
//用户输入
void XieRu() {
int w;
printf("请输入权值 0结束\n");
scanf_s("%d", &w);
while (w) {
Init(w, t); //往数组里面写
printf("请输入权值 0结束\n");
scanf_s("%d", &w);
t++;
}
}
//输出数据
void Prints() {
for (int i = 0; i < t; i++) {
printf("权值为%d \n", arr[i].p->weight);
}
}
//----------------------建立数据结束-----------------------------
//----------------------堆排序-----------------------------------
//落底
void DuiLuoDi(int n, int k) { //n代表长度 k代表节点
int k1 = 2 * k + 1; //设置k1为左边节点
int k2 = 2 * k + 2; //设置k2为右边节点
if (k1 >= n&&k2 >= n) { //判断是不是叶子
return;
}
int a1 = _CRT_INT_MAX; //设置两个最大值
int a2 = _CRT_INT_MAX;
if (k1 < n)a1 = arr[k1].p->weight; //如果左边节点纯在 因为传进来的数字为t t标识的是长度从1开始计数 而堆是从0开始计数的
if (k2 < n)a2 = arr[k2].p->weight; //如果右边节点纯在
if (arr[k].p->weight < a1&&arr[k].p->weight < a2)return; //节点比自己的两个孩子的权值都小 次节点栈排序完成
//否则进行排序
if (a1 < a2) {
ShuZhu p = arr[k]; //定义一个YuanShu
arr[k] = arr[k1]; //将节点与小的元素进行交换
arr[k1] = p;
DuiLuoDi(n, k1); //将k1当做节点往下进行排序
}
else {
ShuZhu p = arr[k];
arr[k] = arr[k2];
arr[k2] = p;
DuiLuoDi(n, k2);
}
}
//初始排序构成堆
void DuiInit() {
for (int i = t / 2; i >= 0; i--) {
DuiLuoDi(t, i);
}
}
//插入元素
void DuiChaRu(HTree *p) { //w代表权值 d代表数据
t++; //数组长度增加
arr[t - 1].p = p;
DuiInit(); //进行排序
}
//取出一个
HTree *DuiOut() {
DuiInit(); //构成堆
HTree *p = arr[0].p;
arr[0] = arr[t - 1];
t--;
DuiLuoDi(t, 0); //只对放入第一个位置的那个数据进行落底操作
return p;
}
//----------------------堆排序-----------------------------------
//----------------------构成哈夫曼树-----------------------------
HTree *HTreeGo() {
DuiInit(); //先构成堆
HTree *H = NULL; //定义一个HTree
int z = t;
for (int i = 0; i < z - 1; i++) {
H = (HTree*)malloc(sizeof(hTree)); //申请空间
H->left = DuiOut(); //在栈里面取一个权值最小的值给新生成的一个节点的左边
H->right = DuiOut(); //在栈里面取一个权值最小的值给新生成的一个节点的右边
H->weight = H->left->weight + H->right->weight; //给新生成的这个节点的权值赋值为左右孩子的权值之和
DuiChaRu(H); //添加这个新的节点到堆中去
}
H = DuiOut(); //上面for循环结结束 最后堆里面还剩一个元素 这个元素就是哈夫曼树的头
return H; //将这个头给返回出去
}
//----------------------构成哈夫曼树-----------------------------
//先序遍历
void XianXu(HTree *H) {
if (H != NULL) {
printf("%d ", H->weight);
XianXu(H->left);
XianXu(H->right);
}
}
//字符编码
//构造哈夫曼编码
void HuffManCoding(HTree * FBT, int len) { //传入一个哈夫曼树和一个长度
static int a[MAXSIZE]; //定义一个数组来装编码
if (FBT != NULL) { //判断存不存在
if (FBT->left == NULL && FBT->right == NULL) { //如果左边和右边都为空那么这个点为叶子
printf("结点的值为%d的编码:", FBT->weight);
for (int i = 0; i < len; i++) //根据长度遍历出编码
printf("%d", a[i]);
printf("\n");
}else { //如果纯在
a[len] = 0; //先往左边走
HuffManCoding(FBT->left, len + 1); //回调 从而往左下走 长度加一(len+1是因为不会改变初始的len的长度 因为往右边回来等下还要用)
a[len] = 1; //从左边回来将这个值改为1
HuffManCoding(FBT->right, len + 1); //回调 从而往右下走
}
}
}
void main() {
XieRu();
Prints();
printf("\n\n先序遍历哈夫曼树\n");
HTree *H;
H = HTreeGo();
XianXu(H);
printf("\n\n哈夫曼树编码\n");
HuffManCoding(H,0);
}