24 哈夫曼树的建立(二叉链表)
作者: 冯向阳时间限制: 10S章节: DS:树
截止日期: 2022-06-30 23:55:00
问题描述 :
内容:请参照二叉树的ADT模板,设计Huffman树的抽象数据类型。(由于该环境目前仅支持单文件的编译,故将所有内容都集中在一个源文件内。在实际的设计中,推荐将抽象类及对应的派生类分别放在单独的头文件中。参考教材、课件,以及网盘中的二叉树ADT原型文件,自行设计Huffman树的ADT。)
应用:要求设计一个算法,使用动态存储的方式,实现Huffman树的ADT。
提示:动态存储是指在新添加一个数据元素时,为它申请一个动态变量。删除一个数据元素时,释放该元素的存储空间。huffman树类的行为主要有两个:构造一棵haffuman树和获取叶子结点的Huffman编码。但由于树上的结点都是动态申请的,为此还需要一个析构函数。每个结点要保存的信息有4个:结点的值、结点的权值、左孩子指针、右孩子指针。
注意:为了保证你生成的哈夫曼树和题目的要求一致,需要遵循以下原则:
1、两个结点合并成一棵树时,权重小的结点为新树的左子树,权重大的结点为新树的右子树。
2、如果两个结点权重相等,则本来在左边的结点为新树的左子树。
3、新创建的树始终放在集合(森林)的最右边。
Huffuman类的参考定义:
/* Huffman树的结点定义 */
template<class ElemType>
struct Huffman_TreeNode{
Huffman_TreeNode *LChild, *RChild; //左、右孩子指针
ElemType data; //结点值
int weight; //结点的权值
Huffman_TreeNode(int w, Huffman_TreeNode *l = NULL, Huffman_TreeNode *r = NULL): weight(w), LChild(l), RChild(r){}
Huffman_TreeNode(const ElemType d, int w, Huffman_TreeNode *l = NULL, Huffman_TreeNode *r = NULL): data(d), weight(w), LChild(l), RChild(r){}
ElemType getData(){ return data;} //取得结点中的数据
};
const int MAX_INT = 32767;
//Huffman树
template<class ElemType>
class Huffman_Tree{
private:
Huffman_TreeNode<ElemType> *root;
public:
//带参数的构造函数
Huffman_Tree(const ElemType *v, const int *w, int size);
//析构函数
~Huffman_Tree(){clear(root);}
//获取根结点
Huffman_TreeNode<ElemType> * GetRoot() const{ return root;}
//前序遍历
bool PreOrderTraverse( Huffman_TreeNode<ElemType> *T, bool (*visit)(Huffman_TreeNode<ElemType> *T) ) const; //前序遍历(递归)
//中序遍历
bool InOrderTraverse( Huffman_TreeNode<ElemType> *T, bool (*visit)(Huffman_TreeNode<ElemType> *T) ) const; //前序遍历(递归)
//后序遍历
bool PostOrderTraverse( Huffman_TreeNode<ElemType> *T, bool (*visit)(Huffman_TreeNode<ElemType> *T) ) const; //前序遍历(递归)
//层次遍历
bool LayerOrderTraverse(bool (*visit)(Huffman_TreeNode<ElemType> *T)) const;
//删除huffman树
void clear(Huffman_TreeNode<ElemType> *t);
//查找值为x的结点的位置 (递归)
void Location_Cursive( Huffman_TreeNode<ElemType> * root, const ElemType &x, Huffman_TreeNode<ElemType> * &location ); //采用先序遍历
//获取父结点(递归)
void GetParent_Cursive(Huffman_TreeNode<ElemType> * parent, Huffman_TreeNode<ElemType> * &x, Huffman_TreeNode<ElemType> * &result, int &flag);
//查找从根结点到元素值为x的叶子结点的路径,路径经过的结点指针存放在顺序栈中(用于获取编码)
void FindPath( Huffman_TreeNode<ElemType> * &x, stack<ElemType> &Q );
};
辅助函数:
(1)Huffuman树遍历用visit函数,显示结点的权值
template<class ElemType>
bool visit(Huffman_TreeNode<ElemType> * root){
if(!root) return false;
else{
if(root->LChild || root->RChild)
cout<<"("<<root->weight<<") ";
else
cout<<root->data<<"("<<root->weight<<") ";
}
return true;
}
(2)Huffman树用栈遍历
template<class ElemType>
void Huffman_Tree_StackTraverse(stack<ElemType> &Q){
char p;
//打印顺序栈
int length = Q.size();
ElemType path[length];
for( int i = 0; i < length; i++){
p = Q.top();
Q.pop();
path[i] = p;
}
for( int i = 0; i < length; i++) cout<<path[i];
}
输入说明 :
第一行:待编码的字符集的个数
第二行:待编码的字符
第三行:字符对应的权值
输出说明 :
第一行:Huffman树的层次遍历结果(非叶子结点显示形式: (结点权值),叶子结点显示形式: 结点data(结点权值) )
第二行:Huffman树的前序遍历结果(非叶子结点显示形式: (结点权值),叶子结点显示形式: 结点data(结点权值) )
第三行:Huffman树的中序遍历结果(非叶子结点显示形式: (结点权值),叶子结点显示形式: 结点data(结点权值) )
第四行:Huffman树的后序遍历结果(非叶子结点显示形式: (结点权值),叶子结点显示形式: 结点data(结点权值) )
第五行:空行
第六行开始:n个叶子结点对应的Huffuman编码(1个叶子结点的Huffman编码占一行)
输入范例 :
-----------------
5
d i a n w
7 5 2 4 9
------------------
输出范例 :
-----------------------------------------------------
(27) (11) (16) i(5) (6) d(7) w(9) a(2) n(4)
(27) (11) i(5) (6) a(2) n(4) (16) d(7) w(9)
i(5) (11) a(2) (6) n(4) (27) d(7) (16) w(9)
i(5) a(2) n(4) (6) (11) d(7) w(9) (16) (27)
d:10
i:00
a:010
n:011
w:11
------------------------------------------------------
#include<iostream>
#include<queue>
using namespace std;
int total;
struct student
{
string code="";
char data=0;
int weight=-1;
int parent=-1;
int left=-1;
int right=-1;
};//这些数据默认为0 等会初始化的时候就更方便 不用再重新给初始化他们赋值为0
void select(student tree[], int n, int& s1, int& s2);
void input_data(student tree[], int n)//输入我们哈夫曼树的数据 但还没开始构建哈夫曼树
{
for (int j = 0; j < n; ++j)
{
cin >> tree[j].data;
}
for (int j = 0; j < n; ++j)
{
cin >> tree[j].weight;
}
}
void creat(student tree[], int n)//构建我们的哈夫曼树 但要用到select
{
int valuemin1 = 0;
int valuemin2 = 0;
for (int i = n; i < total; i++)
{
select(tree, i, valuemin1, valuemin2);
/*该select函数运行过后 作用是给valuemin1 valuuemin2赋值
valuemin1成为了所有还未参与构建哈夫曼树的节点中 拥有最小权重的结点下标
valuemin2成为了所有还未参与构建哈夫曼树的节点中 拥有次小权重的结点下标
*/
//此时i的下标已经是新构建出来的结点了
//以下两行代码的意思比如 有叶子结点 2 3 4 5 当i=4时,说明是新节点 该新节点是由2 3结点构成 所以该结点值为5,2 3的parent是该节点的下标所以2 3的parent等于4
tree[valuemin1].parent = i;
tree[valuemin2].parent = i;
//-----------------------------
tree[i].left = valuemin1;
tree[i].right = valuemin2;
//-------------------------------
tree[i].weight = tree[valuemin1].weight + tree[valuemin2].weight;//i结点的权重为两个子节点的合
}
}
void select(student tree[], int n, int& s1, int& s2) {
int min=0;
for (int i = 1; i <= n; i++)
{
if (tree[i].parent == -1) //为什么要设置这个条件?
//因为parent等于0时 代表这个结点还没参与哈夫曼树的构建 说明还是个零散的结点(散结点)所谓散结点就是还没参与哈夫曼树构造的结点 但你要寻找范围只能是在还没参与的结点里面寻找
{
min = i;
break;
}
}
for (int j = 0; j < n; ++j)
{
if (tree[j].parent == -1) //在散结点中寻找 所谓散结点就是还没参与哈夫曼树构造的结点 这类节点parent为0
{
if (tree[j].weight < tree[min].weight)
min = j;
}
}
s1 = min;
for (int i = 0; i < n; ++i)
{
//首先寻找范围必须是零散的结点 还没参与构建哈夫曼树
//parent=0说明是零散结点
//由于s1的值已经代表第一小结点了 所以i不能等于s1
if (tree[i].parent == -1 && i != s1)
{
min = i;
break;
}
}
for (int j = 0; j <n; ++j)
{
if (tree[j].parent == -1 && j != s1)
{
if (tree[j].weight < tree[min].weight)
min = j;
}
}
s2 = min;//s2是第二小权重结点的代号(下标)
}
void levelscan(student tree[],int n)
{
queue<student> m;
m.push(tree[2 * n - 2]);
student p;
while (!m.empty())
{
p = m.front();
if(p.data!=0)
cout <<p.data;
cout <<"(" << p.weight << ") ";
m.pop();
if (p.left != -1)
{
m.push(tree[p.left]);
}
if (p.right != -1)
{
m.push(tree[p.right]);
}
}
}
void frontscan(student T,student tree[])
{
if (T.data != 0)
{
cout << T.data;
}
cout << "(" << T.weight << ")"<<" ";
if (T.left != -1)
{
frontscan(tree[T.left],tree);
}
if (T.right != -1)
{
frontscan(tree[T.right],tree);
}
}
void midscan(student T, student tree[])
{
if (T.left != -1)
{
midscan(tree[T.left], tree);
}
if (T.data != 0)
{
cout << T.data;
}
cout << "(" << T.weight << ")" << " ";
if (T.right != -1)
{
midscan(tree[T.right], tree);
}
}
void lastscan(student T, student tree[])
{
if (T.left != -1)
{
lastscan(tree[T.left], tree);
}
if (T.right != -1)
{
lastscan(tree[T.right], tree);
}
if (T.data != 0)
{
cout << T.data;
}
cout << "(" << T.weight << ")" << " ";
}
void tt(int m[])
{
for (int i = 0; i < 10; i++)
{
cin >> m[i];
}
}
void Huffman_code(student &T,student tree[],string s="0")
{
if (T.data != '1') {
string a = s.substr(1, s.size() - 1);
T.code += a;
}
if (T.left != -1) Huffman_code(tree[T.left],tree, s + "0");
if (T.left != -1) Huffman_code(tree[T.right], tree, s + "1");
}
int main()
{
int n = 0;
cin >> n;
total = 2 * n - 1;
student tree[100];
input_data(tree, n);//输入我们哈夫曼树的数据 但还没开始构建哈夫曼树
creat(tree, n);//构建我们的哈夫曼树
levelscan(tree, n);
cout << endl;
frontscan(tree[2 * n - 2],tree);
cout << endl;
midscan(tree[2 * n - 2], tree);
cout << endl;
lastscan(tree[2 * n - 2], tree);
cout << endl;
Huffman_code(tree[2 * n - 2], tree);
cout << endl;
for (int i = 0; i < n; ++i)
{
if (tree[i].data != '1')
{
cout << tree[i].data << ":" << tree[i].code;
if(i!=n-1)
cout << endl;
}
}
return 0;
}