哈夫曼树---建立操作

在这里插入图片描述 这里演示建树过程:
在这里插入图片描述 左小,右大。
思路:

找到最小和次小,合并形成一个新的父节点,(所以一个结点有三个指针域)
从叶子往上建树的过程。

所以关键是:1.找到最小和次小,
                      2.  合并
下面展示过程代码:
template <typename T, typename E>//一个结点自带三个指针
struct HuffmanNode 
{
	E data;
	HuffmanNode<T, E>* leftChild, * rightChild, * parent;
	HuffmanNode() :leftChild(NULL), rightChild(NULL), parent(NULL) {}//构造函数
	HuffmanNode(E elem, HuffmanNode<T, E>* pr, HuffmanNode<T, E>* left, HuffmanNode<T, E>* right)
		: data(elem),parent(pr), leftChild(left), rightChild(right) {}
	//重载操作符:
	bool operator > (HuffmanNode<T, E> right) {
		return data > right.data;
	}
	bool operator >= (HuffmanNode<T, E> right) {
		return (data > right.data) || (data == right.data);
	}
	bool operator < (HuffmanNode<T, E> right) {
		return data < right.data;
	}
	bool operator <= (HuffmanNode<T, E> right) {
		return (data < right.data) || (data == right.data);
	}
	bool operator == (HuffmanNode<T, E> right) {
		return data == right.data;
	}
};

下面生成哈夫曼树

//Huffman树类定义
template <typename T, typename E>
class HuffmanTree {
public:
	HuffmanTree(E w[], int n);
	~HuffmanTree() {  deleteTree(root);}
	HuffmanNode<T, E>* getRoot() {return root;}
	void output(HuffmanNode<T, E>* t, string str, ostream& out);
	void preorder(HuffmanNode<T, E>* t);
protected:
	HuffmanNode<T, E>* root;
	void deleteTree(HuffmanNode<T, E>* t);
	void mergeTree(HuffmanNode<T, E> *bt1, HuffmanNode<T, E> *bt2,
		HuffmanNode<T, E>*& parent);
};

用数组初始化该树,就不用一个一个输入数字。

第一种建树方式

1.把哈夫曼结点保存在堆中
2.用first 和second new两个结点,重新保存堆中的结点值,把左右孩子的父节点连上自己。
3.合并first,second, 父节点的左右指针连接,数据重新计算。
4.再插入堆中
5.最后把父节点从堆中拿出,只要值、


void HuffmanTree::mergeTree(HuffmanNode *ht1, HuffmanNode *ht2, HuffmanNode &parent)
{
     parent.leftchild=ht1;
     parent.rightchild=ht2;
     parent.data = ht1->data+ht2->data;
}

HuffmanTree::HuffmanTree (float w[ ] , int n )
{ 
	 MinHeap<HuffmanNode>  hp;
	 int i ;

     HuffmanNode parent , *first, *second,work; 
     
    for ( i = 0; i < n; i++ ) 
    {
       work.data=w[i];
       work.leftchild =work.rightchild = work.parent=0; 
       hp.Insert(work);  
    }      //传送初始权值
	for ( i = 0; i < n-1; i++ ) 
    {
         //建立哈夫曼树的过程,做n-1趟
		first=new HuffmanNode;
		second=new HuffmanNode;
		hp.RemoveMin ( *first );          //选根权值最小的树
        hp.RemoveMin ( *second );      //选根权值次小的树
		if (first->leftchild)  first->leftchild ->parent =first;
		if (second->leftchild) second->leftchild ->parent =second;
        mergeTree( first, second,parent ); //合并
        hp.Insert ( parent );	     //重新插入到小根堆中
    }
    root=new HuffmanNode;             
	*root=parent;                     //最后的结点为根结点,传值 
}

第二种方式:

首先初始化
思路:用数组,先建立一个最小堆,
便于找到最小和次小
然后合并,
这里需要一个merge函数。

//构造函数
template <typename T, typename E>
HuffmanTree<T, E>::HuffmanTree(E w[], int n) 
{
	//给出n个权值w[1]~w[n], 构造Huffman树
	MinHeap<T, HuffmanNode<T, E>> hp(20);
	HuffmanNode<T, E>* parent=0, *first, *second, work;
	HuffmanNode<T, E>* NodeList/* = new HuffmanNode<T,E>[n]*/; 
	//不宜一次性建立森林,否则析构函数一个一个删结点时操作系统断言出错
	int i;
	for (i = 0; i < n; i++) {//按棵逐步建立森林中的树木,并作为Huffman树的叶结点。数据放入森林
		NodeList = new HuffmanNode<T, E>;
		NodeList->data = w[i + 1];
		NodeList->leftChild = NULL;
		NodeList->rightChild = NULL;
		NodeList->parent = NodeList;//父指针指向自己,信息入堆后,出堆时可以找到对应结点
		hp.Insert(*NodeList);	//森林信息插入最小堆中
	}
	for (int i = 0; i < n - 1; i++)
	{
		hp.RemoveMin(work);
		first = work.parent;			//first指向对应的最小结点,parent指向的是自己。
		hp.RemoveMin(work);	        //根权值次小的树
		second = work.parent;			//second指向对应的次小结点
		mergeTree(first, second, parent);
		hp.Insert(*parent);
	}
	root = parent;
}

这里merge函数的定义
需要把孩子结点的父指针指向父节点。
父节点的数值是两个孩子结点值的和。

template <typename T, typename E>
void HuffmanTree<T, E>::mergeTree(HuffmanNode<T, E> *bt1,HuffmanNode<T, E>*bt2, HuffmanNode<T, E>*& parent)
{
	//parent必须为引用,它作为返回量
	//在函数中形成
	parent = new  HuffmanNode<T, E>;
	parent->leftChild = bt1;
	parent->rightChild = bt2;
	parent->parent = parent;
	parent->data = bt1->data + bt2->data;
	bt1->parent = bt2->parent = parent;
}

第三种方式

用一个新的结构体的形式,保存值的大小,和哈夫曼结点的地址的指针。

struct node
{
   float data;
   HuffmanNode *link;
};
void HuffmanTree::mergeTree(HuffmanNode *ht1, HuffmanNode *ht2, node &node1)
{    HuffmanNode *parent;

     parent=new HuffmanNode;
     parent->leftchild=ht1;
     parent->rightchild=ht2;
     parent->data = ht1->data+ht2->data;
     ht1->parent= ht2->parent=parent;

	 node1.data=parent->data;
	 node1.link=parent;


}


HuffmanTree::HuffmanTree (float w[ ] , int n )
{ 
	 MinHeap<node>  hp;
	 int i ;

     HuffmanNode  *first, *second,*work; 
     node node1;

    for ( i = 0; i < n; i++ ) 
    {
       work=new HuffmanNode;
	   work->data=w[i];
       work->leftchild =work->rightchild = work->parent=0; 
	   node1.data=w[i];
	   node1.link=work;
       hp.Insert(node1);  
    }      //传送初始权值
	for ( i = 0; i < n-1; i++ ) 
    {
         //建立哈夫曼树的过程,做n-1趟
		hp.RemoveMin ( node1 );       //选根权值最小的树
		first=node1.link;
        hp.RemoveMin ( node1 );  //选根权值次小的树
		second=node1.link ;

        mergeTree( first, second,node1 ); //合并
        hp.Insert ( node1 );	     //重新插入到小根堆中
    }
	root=node1.link;    //最后的结点为根结点 

}

将建好的树,输出到文件中。

template <typename T, typename E>
void HuffmanTree<T, E>::output(HuffmanNode<T, E>* t, string str, ostream& out) {//按目录结构方式输出二叉树,添加
	if (!t) {
		return;
	}
	out << str << t->data;
	if (t->leftChild) {
		out << "─┐" << endl;
		if (t->rightChild) {
			output(t->leftChild, str + "│ ", out);
		}
		else {
			output(t->leftChild, str + "  ", out);
		}
	}
	if (t->rightChild) {
		out << endl << str << "└─┐" << endl;
		output(t->rightChild, str + "  ", out);
	}
}
int main()
{
	int w[7] = { 3,4,5,2,1,7,9 };
	string s;
	HuffmanTree<int, int >hp(w,5);
	hp.preorder(hp.getRoot());
	
	ofstream out("CodeFile.txt", ios::binary);
	hp.output(hp.getRoot(),s,out);

	return 0;
}

在这里插入图片描述
哈夫曼编码

首先需要统计编码的字符种类以及个数。
有字符数组的集合之后,可以建立哈夫曼树。
进行编码
因为每一个字符对应一个编码
则这些编码可以保存到文件中。
再进行译码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值