学数据结构第七周老师布置的作业:
(小白历经千辛万苦才ac,哭)
题目:
哈夫曼编码构造算法:
(老师害挺好的,直接把算法给贴上了,但其实上课都讲过(小声)
【步骤1】由给定的 n 个字母 {C0,C1,C2,…, Cn-1}和权值 {W0,W1,W2,…, Wn-1},
构造具有n棵扩充二叉树的森林F = { T0,T1,T2,…, Tn-1 },
其中每棵扩充二叉树Ti只有一个带权值Wi的根结点,其左、右子树均为空;
【步骤2】在F中选取两棵根结点权值最小的扩充二叉树,作为左、右子树构造一棵新的二叉树,
并置新的二叉树的根结点的权值为其左、右子树上根结点的权值之和
【步骤3】在F中删去这两棵二叉树;
【步骤4】把新的二叉树加入F中;
【步骤5】重复步骤2~步骤4, 直到F中仅剩下一棵二叉树为止,即为所求哈夫曼树。
(说白了,就是不停的找两个权值最小的然后把他们链接到一个根节点上。为什么要找最小值,是因为哈夫曼树本身要实现的功能)
输入格式(这个好烦)
第一行:结点的数量
第二行:各个结点的权值
输出格式
第一行:计算该哈夫曼树的WPL
第二行:按照括号的形式输出其哈弗曼树(参考样例输出格式)
第三行:按照哈夫曼编码的由左子树到右子树输出对应权重的编码,不同编码中间用空格隔开
(为了减轻难度,最后一个编码后面需加上空格)(如果编码长度相等,从左到右按照0到1的格式输出,参考样例输出格式)
样例输入:
5
7 5 3 1 8
样例输出: //这个好像其实就是一个遍历的过程
52
24(9(4(1,3),5),15(7,8)) //调试的时候猛然发现这个数字的顺序和前序遍历的顺序是一样的
000 001 01 10 11
小白的思考:
1.只写这个题就不用写类了,累了,好像还没啥必要叭,用结构体好了。
struct TreeNode {
TreeNode* Leftchild;
int value;
TreeNode* Rightchild;
};
2.构造树的函数需要:
3.求WPL的函数:
4 第二个输出:发现他的顺序和前序遍历的一样,所以在前序遍历的函数中加入符号就好了。然后根据题目可知,在遍历左子树前加"(",左子树右子树之间加",“右子树遍历后加”)"
5.哈夫曼编码:
同样也是递归,需要记录深度,然后根据深度输出,遍历左子树前赋值1,遍历右子树前赋值0.
#include
struct TreeNode {
TreeNode* Leftchild;
int value;
TreeNode* Rightchild;
};
//建树
TreeNode* creat(int n) {
TreeNode* Store[30] = { NULL };
TreeNode* temp;
for (int i = 0; i < n; i++) {
temp = new TreeNode;
std::cin >> temp->value;
temp->Leftchild = NULL;
temp->Rightchild = NULL;
Store[i] = temp;
}
int flag = 0;
TreeNode* p = NULL;
while (!flag) {
//遍历两次确实比较好写,但是好傻哦
int Min1 = 0, Min2 = 0;
int count = 0;
for (int i = 0; i < n; i++) { //这个循环就可以保证Min1指的是最小的数
if (Store[i] != NULL) {
if ((Store[Min1]->value) > Store[i]->value)
Min1 = i;
}
else continue;
}
if (Min1 == 0) Min2 = 1;
for (int i = 0; i < n; i++) {
if (i == Min1) continue;
if (Store[Min2] == NULL && Min2 < n) Min2++;
if (Store[i] != NULL && Store[Min2] != NULL) {
if (Store[i]->value < Store[Min2]->value)
Min2 = i;
}
else continue;
}
//这两个循环确实可以保证Min1最小Min2第二小
temp = new TreeNode;
temp->value = Store[Min1]->value + Store[Min2]->value;
temp->Leftchild = Store[Min1];
temp->Rightchild = Store[Min2];
Store[Min1] = temp;
Store[Min2] = NULL;
for (int i = 0; i < n; i++) {
if (Store[i] != NULL) {
p = Store[i];
count++;
}
else continue;
}
if (count == 1) {
return p;
}
}
}
//前序遍历
void Preorder(TreeNode* tree) { //前序遍历证明这个树没啥问题
if (tree == NULL) return;
std::cout << tree->value;
if (tree->Leftchild) {
std::cout << "(";
Preorder(tree->Leftchild);
std::cout << ",";
}
if (tree->Rightchild) {
Preorder(tree->Rightchild);
std::cout << ")";
}
}
//求WPL
int WPL(TreeNode* tree, int length)
{
TreeNode* p = tree;
if (p==NULL) //判断是不是空树
return 0;
else //如果不是空树
{
if (!p->Leftchild && !p->Rightchild)
return p->value * length;
else
return WPL(p->Leftchild, length + 1) + WPL(p->Rightchild, length + 1);
}
}
void Huffmancode(TreeNode* huff, int length)
{
TreeNode* p = huff;
static int code[30];
int i;
if (p)
{
if (!p->Leftchild && !p->Rightchild) //左右都是空的,找到叶子结点了!!!
{
for (i = 0; i < length; i++)
std::cout << code[i];
std::cout << " ";
}
else
{
code[length] = 0;
Huffmancode(p->Leftchild, length + 1);
code[length] = 1;
Huffmancode(p->Rightchild, length + 1);
}
}
}
int main() {
int n;
std::cin >> n;
TreeNode* tree;
tree = creat(n);
int wpl;
wpl = 0;
wpl = WPL(tree, wpl);
std::cout << wpl << std::endl;
Preorder(tree);
std::cout << "\n";
int length = 0;
Huffmancode(tree, length);
}
找最小数次小数,用一次循环可以解决的算法: 嗯,就先酱叭,感觉还是不太好,但是整整花了10小时去学二叉树和哈夫曼,过程中确实收获了很大的进步。 继续努力学习!!!
Min1储存最小数,Min2储存此小数。Min1和数组(Store[])中i元素比较,如果Store[i]