通过根据数据出现的次数计算权值来规划树结构,提高树的查找性能。这样设计出来的树最常用的数据放到离根节点近的地方,从而提高查找速度。带权路径长度最短的树叫做最优二叉树也叫做哈夫曼树。
一些术语:
结点路径长度:从根节点到当前节点的边数。
数的路径长度:所有叶子结点的结点路径长度之和
结点带权路径长度:结点权值*结点路径
树的带权路径长度:所有叶子结点的结点带权路径长度之和
实现思路:
1 首先需要一个结点数组来按照优先级对数据元素进行排序。
2 编码通过数据出现的次数对数据进行编码。
3 解码通过编码推出数据。
结点结构体内容:
左节点位置,右结点位置,双亲位置,结点名称,权值.
哈夫曼树结构构造:
1 对传入的数据进行统计,使用ASSIC码来进行数据出现次数统计。得到权值
2 结点构造初始化:左右和双亲节点值均设置为0。
3 遍历结点数组找到权值最小的两个结点,称为最小结点和次结点,将两节点权值相加构造一个新的结点作为parent结点。最小结点作为左孩子,次小结点作为右孩子。依次寻找,直到数组中所有结点均有parent结点结束。结束条件是所有结点parent!=0。
4 赫夫曼编码:
1 前提需要一个char类型的数组来对每个结点的编码值进行存储,char类型的数组设计,数组长度设置为n,最大索引值使用temp变量存储为n-1,假设char数组为hc,设置hc[n] = '\0',初始化数组所有值为0。
2 从叶子结点开始对树进行构造。从数组结点开始根据索引取出结点判断结点为parent结点的左/右孩子,左节点编码设置为0右节点编码设置为1。并设置hc[temp--]=1/0;同时设置新的结点为当前结点的父节点,parent结点为当前结点的父节点的parent结点,循环直到该树的根节点结束。此时得到的数组为数组结点中结点的赫夫曼编码。
3 输出赫夫曼编码,遍历数组即可。结点数组中的结点索引和存放赫夫曼编码的数组中的索引是相同的。找特定节点的赫夫曼编码只需要找到结点数组中该节点的索引即可。
下面代码展示
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_TREE_SIZE 1000
typedef struct {
int weight;
int parent;
int left_child;
int right_child;
} HuffmanNode, *HuffmanTree;
typedef struct {
char ch;
char code[MAX_TREE_SIZE];
} HuffmanCode, *HuffmanTable;
void createHuffmanTree(HuffmanTree tree, int n) {
int i, j, k;
int min1, min2;
for (i = 0; i < 2 * n - 1; i++) {
tree[i].weight = 0;
tree[i].parent = -1;
tree[i].left_child = -1;
tree[i].right_child = -1;
}
for (i = 0; i < n; i++) {
printf("请输入第%d个节点的权重:", i + 1);
scanf("%d", &tree[i].weight);
}
for (i = 0; i < n - 1; i++) {
min1 = min2 = MAX_TREE_SIZE;
k = 0;
for (j = 0; j < n + i; j++) {
if (tree[j].parent == -1 && tree[j].weight < min1) {
min2 = min1;
min1 = tree[j].weight;
k = j;
} else if (tree[j].parent == -1 && tree[j].weight < min2) {
min2 = tree[j].weight;
}
}
tree[k].parent = n + i;
tree[n + i].weight = min1 + min2;
tree[n + i].left_child = k;
k = 0;
for (j = 0; j < n + i; j++) {
if (tree[j].parent == -1 && tree[j].weight < min1) {
min2 = min1;
min1 = tree[j].weight;
k = j;
} else if (tree[j].parent == -1 && tree[j].weight < min2) {
min2 = tree[j].weight;
}
}
tree[k].parent = n + i;
tree[n + i].right_child = k;
}
}
void printHuffmanTree(HuffmanTree tree, int n) {
int i;
printf("节点\t权重\t父节点\t左孩子\t右孩子\n");
for (i = 0; i < 2 * n - 1; i++) {
printf("%d\t%d\t%d\t%d\t%d\n", i + 1, tree[i].weight, tree[i].parent + 1, tree[i].left_child + 1, tree[i].right_child + 1);
}
}
void createHuffmanTable(HuffmanTree tree, HuffmanTable table, int n) {
int i, j, k;
char code[MAX_TREE_SIZE];
int parent, child;
for (i = 0; i < n; i++) {
table[i].ch = i + 'a';
code[0] = '\0';
parent = tree[i].parent;
child = i;
while (parent != -1) {
if (tree[parent].left_child == child) {
strcat(code, "0");
} else {
strcat(code, "1");
}
child = parent;
parent = tree[parent].parent;
}
strrev(code);
strcpy(table[i].code, code);
}
}
void printHuffmanTable(HuffmanTable table, int n) {
int i;
printf("字符\t编码\n");
for (i = 0; i < n; i++) {
printf("%c\t%s\n", table[i].ch, table[i].code);
}
}
int main() {
int n;
printf("请输入节点数:");
scanf("%d", &n);
HuffmanTree tree = (HuffmanTree)malloc(sizeof(HuffmanNode) * (2 * n - 1));
createHuffmanTree(tree, n);
printHuffmanTree(tree, n);
HuffmanTable table = (HuffmanTable)malloc(sizeof(HuffmanCode) * n);
createHuffmanTable(tree, table, n);
printHuffmanTable(table, n);
free(tree);
free(table);
return 0;
}
代码中求权值我做的操作时直接让用户输入权值,这个可以该成上面我说的使用ASSIC码来计算权重。代码:
int * arr = (int8)malloc(sizeof(int)*256);
for(int i; i < 256; i++){
arr[i] = 0;
}
for(int i; inputstring[j] != '\0'; j++){
arr[(unsigned char) inputstring[j]]++;
}