建立Huffman树的基本思路:给定有权重的一系列数据(带权重),从中挑选最小权重的两个数据,组成一棵树,得到的父节点再插入到数据系列当中。
开始的时候按着严老师的办法,是借助顺序表来完成Huffman树的建立;同样,在建树过程中要从顺序表中选择比较小的两个数,相加后再插入到表尾,如此往复,知道所有给出的点都插入为止。
通过最小堆来建树也很灵活便捷。堆的性能高,排序时间复杂度为nlog(2)n,利用最小堆,就可以将很快找出最小的元素(总是在顶部)。
下面8步立刻掌握利用最小堆来建立Huffman树。
看图解说
①原图(已经是最小堆);
②交换堆的首元素(肯定是最小的)和最后一个元素对换;
③交换后删除最后一个元素(复制出来),其实也不是真正的删除,只是size-1这个操作而已;调换后就不是堆了,重新调整heap为最小堆,调整可以用递归调用,也可以用下标作为判断条件,我的代码用的是下标判断条件;
④继续②;
⑤弹出最小的,调整;将两个最小点复制出来后,组成新的父节点,调整他们之间的内部指针关系;
⑥调整后;
⑦插入新节点,调整;
⑧调整后。
以上做了第一遍的操作,继续做下去知道堆中只剩下最后一个元素为止,再将root指向其就可以了,下面是代码。如此下来,Huffman树就建好了。
Huffman节点数据结构设计:
#ifndef _HUFFMANNODE_H_ #define _HUFFMANNODE_H_ struct HuffmanNode //Huffman节点定义 { private : int data; public : //构造函数 HuffmanNode * left,* right,* parent; HuffmanNode():left(NULL),right(NULL),parent(NULL),data(-1){} HuffmanNode( int d):left(NULL),right(NULL),parent(NULL),data(d){} //重载运算符 HuffmanNode &operator=( const HuffmanNode& hn) { left = hn.left; right = hn.right; data = hn.data; return * this ; } //数据的获取和维护 int GetData() const { return data;} //获取数据 bool SetData( int d){data = d; return true ;} //设置数据 }; #endif |
最小堆类:
#include <iostream> using namespace std; #ifndef _MINHEAP_H_ #define _MINHEAP_H_ #include "HuffmanNode.h" const int DefaultSize = 100; class MinHeap { HuffmanNode * heap; int szCurrent; public : MinHeap( int sz = DefaultSize); ~MinHeap() { delete [] heap;} void CreateMinHeap( int arr[], int n); //数组构建最小堆 bool Insert(HuffmanNode * e); //往堆中插入Huffman节点 void SiftDown( int start, int m); //下滑,重建最小堆 void SiftUp( int start, int m); //上滑,在插入的时候用到 HuffmanNode * GetMinNode(); //获取Huffman节点data值最小的节点,同时维护szCurrent bool SwapNode( int i, int j); //交换下标为i和j的Huffman节点 void Print(); //打印Huffman节点 }; #endif #include <iostream> using namespace std; #include "MinHeap.h" #include <assert.h> MinHeap::MinHeap( int sz) { heap = new HuffmanNode[sz]; assert (heap!=NULL); szCurrent = 0; } void MinHeap::CreateMinHeap( int arr[], int n) { this ->heap = new HuffmanNode[DefaultSize]; assert (heap!=NULL); int i; for (i=0; i<n; i++) heap[i].SetData(arr[i]); szCurrent = n; int currentpos = (szCurrent-2)/2; //从最后一个顶点开始调整 while (currentpos >= 0) { SiftDown(currentpos,szCurrent-1); currentpos -- ; } } void MinHeap::SiftDown( int start, int m) { int i = start,j = i*2+1; HuffmanNode temp = heap[i]; while (j<=m) { if (j<m && heap[j].GetData() > heap[j+1].GetData()) //j记录比较小的子节点 j++; if (temp.GetData() <= heap[j].GetData()) //不调整 break ; else { heap[i] = heap[j]; //子节点上移 i = j; j = 2*j+1; } } heap[i] = temp; } void MinHeap::SiftUp( int start, int m) { int j = start, //子节点位置 i = (start-1) / 2; //顶点位置 HuffmanNode temp = heap[j]; //记录子节点 while (j > 0) { if (temp.GetData() > heap[i].GetData()) //不调整 break ; else { heap[j] = heap[i]; //顶点下滑 j = i; i = (i-1) / 2; } } heap[j] = temp; } void MinHeap::Print() { for ( int i=0; i<szCurrent; i++) cout << heap[i].GetData() << " " ; cout << endl; } bool MinHeap::Insert(HuffmanNode * e) { szCurrent++; if (szCurrent > DefaultSize) abort (); heap[szCurrent-1] = *e; SiftUp(szCurrent-1,0); //调整 return true ; } HuffmanNode * MinHeap::GetMinNode() { if (szCurrent>0) { HuffmanNode * t; SwapNode(0,szCurrent-1); //此时heap[0].data是最小的,让它跟最后一个元素调换 szCurrent--; SiftDown(0,szCurrent-1); t = new HuffmanNode(); * t = heap[szCurrent]; cout << "GetMinNode()后得到的堆:" ; Print(); return t; } return NULL; } bool MinHeap::SwapNode( int i, int j) { swap(heap[i],heap[j]); return true ; } |
Huffman类:
#include "MinHeap.h" #ifndef _HUFFMANTREE_H_ #define _HUFFMANTREE_H_ class Huffman { MinHeap * mh; //堆,协助建立Huffman树 HuffmanNode * root; //Huffman树的根节点 public : Huffman():root(NULL){}; ~Huffman() { delete mh; MakeEmpty(root); } void MakeEmpty(HuffmanNode * ptr) { if (ptr->left) MakeEmpty(ptr->left); if (ptr->right) MakeEmpty(ptr->right); delete ptr; } void CreateHuffmanTree( int arr[], int size); void Print(); }; #endif #include <iostream> using namespace std; #include <assert.h> #include "HuffmanTree.h" #include "MinHeap.h" #include <queue> void Huffman::CreateHuffmanTree( int arr[], int size) { mh = new MinHeap(size); mh->CreateMinHeap(arr,size); //将数据建立最小堆 int i; HuffmanNode * left; HuffmanNode * right; HuffmanNode * parent; for (i=0; i<size-1; i++) { left = mh->GetMinNode(); //较小成为左孩子 right = mh->GetMinNode(); //较大成为右孩子 //这里可以归结出一个函数来,但是有点麻烦,直观点 parent = new HuffmanNode(left->GetData()+right->GetData()); parent->left = left; parent->right = right; left->parent = right->parent = parent; if (!mh->Insert(parent)) abort (); cout<< "插入父节点之后的堆:" ; mh->Print(); } root = parent; } void Huffman::Print() { queue<HuffmanNode *> q; q.push(root); HuffmanNode * p; while (!q.empty()) { p = q.front(); cout << p->GetData() << " " ; if (p->left != NULL) q.push(p->left); if (p->right != NULL) q.push(p->right); q.pop(); } } |
Huffman(哈弗曼,赫夫曼)在通信领域用途很大,在文件压缩技术也有所运用,做些笔记,以后有用,与大家共享,欢迎讨论:)。