[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程

二话不多说,首先我们先来看个霍夫曼编码树的栗子。

假设我们想要压缩的是这个字符串:

“beep boop beer!”

首先统计它们的出现次数,得到下面这张表:

然后根据出现的频率依次排序,放在一个优先队列Priority Queue中

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第1张图片

接下来的任务就是把这个Priority Queue转换成二叉树。

我们始终从Priority Queue的head取两个元素来构造一个二叉树

注意,第一个元素是左结点,第二个元素是右结点

并把这两个元素的权重相加,并放回Priority中

再次注意,这里的Priority是字符出现的次数)

然后,我们得到下面的数据图表:

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第2张图片

重复上面的步骤,我们再把前两个取出来,

形成一个Priority为2+2=4的结点,然后再放回Priority Queue中 :

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第3张图片


继续重复上面的步骤我们可以看到和初始化最大堆最小堆一样,这是一种自底向上的建树的过程

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第4张图片

然后继续重复

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第5张图片

重复

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第6张图片

再再重复

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第7张图片

此时,我们把这个树的左支编码为0,右支编码为1,

这样我们就可以遍历这棵树得到字符的编码,

比如:‘b’的编码是 00,’p’的编码是101, ‘r’的编码是1000。

我们可以看到出现频率越多的会越在上层,编码也越短,出现频率越少的就越在下层,编码也越长


最终我们可以得到下面这张编码表:

[C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程_第8张图片

这里需要注意一点,当我们encode的时候,我们是按“bit”来encode,decode也是通过bit来完成,

比如,如果我们有这样的bitset “1011110111″ 那么其解码后就是 “pepe”。

所以,我们需要通过这个二叉树建立我们Huffman编码和解码的字典表。

这里需要注意的一点是,我们的Huffman对各个字符的编码是不会冲突的,

也就是说,不会存在某一个编码是另一个编码的前缀,不然的话就会大问题了。

因为encode后的编码是没有分隔符的。

于是,对于我们的原始字符串  beep boop beer!

其对就能的二进制为 :

0110 0010 0110 0101 0110 0101 0111 0000 0010 0000 0110 0010 0110 1111 0110

1111 0111 0000 0010 0000 0110 0010 0110 0101 0110 0101 0111 0010 0010 0001

我们的Huffman的编码为:

0011 1110 1011 0001 0010 1010 1100 1111 1000 1001

从上面的例子中,我们可以看到被压缩的比例还是很可观的。



下面的源码是一个通过最小堆创建霍夫曼编码树并且输出对应的霍夫曼编码的案例

#include <iostream>
using namespace std;
const int MaxN = 100;


template <class T> 
class MinHeap
{
public:
	MinHeap(int maxSize);
	MinHeap(T a[], int n);
	~MinHeap()
	{
		delete []heapArray;
	}
	int Insert(T &d);//最小堆的插入方法
	T * DeleteMin();//删除最小堆的堆顶元素

	T * heapArray;//存放数据的数组
	T * imputArray;//存放输入的数组
	T * saveNode[100];
	int saveNodeCount;//已存储节点的数目
	int heapCurrentSize;
	int heapMaxSize;
	void FilterDown(int p);
	void FilterUp(int p);
};

template <class T> MinHeap <T> :: MinHeap(int maxSize)
{
	heapMaxSize = maxSize;
	heapArray = new T [heapMaxSize];
	heapCurrentSize = 0;
}

//给定数组构成最小堆
template<class T> MinHeap<T>::MinHeap(T a[],int n)
{
	heapMaxSize = n;
	heapArray = new T[heapMaxSize+1];
	saveNodeCount=n;
	for(int j=0;j<n;j++)
		heapArray[j]=a[j];
	imputArray=a;
	heapCurrentSize=n;
	int i=(heapCurrentSize-2)/2;
	while(i>=0)
	{
		FilterDown(i);
		i--;
	}
	if (heapMaxSize%2==0){
		for(int k=0;k<n;k++){
			heapArray[k]=heapArray[k+1];
		}
	}
}
template <class T> void MinHeap<T>::FilterDown(const int start)
{
	int i=start,j;
	T temp=heapArray[i];
	j=2*i+1;
	while(j<=heapCurrentSize-1)
	{
		if(j<=heapCurrentSize-1 && heapArray[j].data>heapArray[j+1].data) j++;
		if(temp.data <=heapArray[j].data) break;
		else {heapArray[i]=heapArray[j];i=j;j=2*j+1;}
	}
	heapArray[i]=temp;
}
template<class T> int MinHeap<T>::Insert(T&d)
{
	heapArray[heapCurrentSize]=d;
	heapArray[heapCurrentSize].arrkey=saveNodeCount;
	FilterUp(heapCurrentSize);
	heapCurrentSize++;
	saveNode[saveNodeCount++]=&d;
	return 1;
}
template <class T> void MinHeap<T>::FilterUp(int p)
{
	int j=p,i;
	T temp = heapArray[j];
	i=(j-1)/2;
	while(j>0)
	{
		if(heapArray[i].data<=temp.data) break;
		else
		{
			heapArray[j]=heapArray[i];
			j=i;i=(j-1)/2;
		}
	}
	heapArray[j]=temp;
}
template<class T> T * MinHeap<T>::DeleteMin()
{
	T * temp=new HuffmanTreeNode<double>;
	(*temp)=heapArray[0];
	heapArray[0]= heapArray[heapCurrentSize-1];
	heapCurrentSize--;
	FilterDown(0);
	if(temp->arrkey>-1 && temp->arrkey<heapMaxSize)
		return &imputArray[temp->arrkey];
	else if(temp->arrkey>=heapMaxSize && temp->arrkey<100)
		return saveNode[temp->arrkey];
	return NULL;
}

template <class T> class HuffmanTree;
template <class T> class HuffmanTreeNode
{
	friend class HuffmanTree;
public:
	int arrkey;
	T data;
	HuffmanTreeNode <T>  *leftChild, *rightChild, *parent;
};
template <class T> class HuffmanCodeNode
{
	friend class HuffmanTree;
public:
	HuffmanTreeNode <T> *dataptr;
	int bit[MaxN];
	int start;
};
template <class T> class HuffmanTree
{
	friend class HuffmanTreeNode;
	friend class HuffmanCodeNode;
public:
	HuffmanTree(){};
	HuffmanTree(T weight[], int n);
	HuffmanCode();
protected:
	HuffmanTreeNode <T> *hfTree;
	HuffmanCodeNode <T> *hfCode;
	int currentSize;
	void MergeTree(HuffmanTreeNode <T> &bt1, HuffmanTreeNode <T>
		&bt2, HuffmanTreeNode <T> *pt)
	{
		pt->leftChild = &bt1; pt->rightChild = &bt2;
		pt->data = bt1.data + bt2.data;
		pt->parent = NULL; bt1.parent = pt; bt2.parent = pt;
	}
};
template <class T> HuffmanTree <T> :: HuffmanTree(T weight[], int n)
{
	HuffmanTreeNode <T> *firstchild, *secondchild, *parent;
	HuffmanTreeNode <T> *TNode;
	if(n > MaxN)
	{
		cout<<"Error!"<<endl;exit(0);
	}
	currentSize = n;
	hfCode = new HuffmanCodeNode <T> [n];
	TNode = new HuffmanTreeNode <T> [n];
	for(int i = 0;i < n;i++)
	{
		hfCode[i].dataptr = &TNode[i];
		TNode[i].data = weight[i];
		TNode[i].arrkey = i;
		TNode[i].parent = TNode[i].leftChild = TNode[i].rightChild = NULL;
	}
	MinHeap <HuffmanTreeNode <T> > hp(TNode,n);
	for(i = 0;i < n-1;i++)
	{
		parent = new HuffmanTreeNode <T>;
		firstchild = hp.DeleteMin();
		secondchild = hp.DeleteMin();
		MergeTree(*firstchild, *secondchild, parent);
		hp.Insert(*parent);
	}
	hfTree = parent;
}
template <class T> HuffmanTree <T> :: HuffmanCode()
{
	HuffmanCodeNode <T> *cd = new HuffmanCodeNode<T>;
	HuffmanTreeNode <T> *child, *parent;
	for(int i=0;i < currentSize;i++)
	{
		cd->start = currentSize -1;
		child = hfCode[i].dataptr;
		parent = child->parent;
		while(parent != NULL)
		{
			if(parent->leftChild == child)
				cd->bit[cd->start] = 1;
			else
				cd->bit[cd->start] = 0;
			child = parent;
			parent = parent->parent;
			cd->start--;
		}
		for(int k=0;k < currentSize;k++)
		{
			hfCode[i].bit[k] = cd->bit[k];
			hfCode[i].start = cd->start+1;
		}
	}
	cout<<endl<<"输出:"<<endl;
	for(i=0;i<currentSize;i++)
	{       cout<<hfCode[i].dataptr->data<<":  ";
	for(int j=hfCode[i].start;j<currentSize;j++)
		cout<<hfCode[i].bit[j];
	cout<<endl;
	}
}

int main()
{
	double weight[MaxN];
	int n;
	cout<<"How many numbers are you going to input?"<<endl;
	cin >> n;
	cout<<"Input:"<<endl;
	for(int i=0;i<n;i++)
		cin>>weight[i];
	HuffmanTree <double> a(weight,n);
	a.HuffmanCode();
	return 0;
}


你可能感兴趣的:([C++]数据结构:使用最小堆创建霍夫曼编码树HuffmanTree的过程)