##資料結構##如何建立一棵哈夫曼樹並實現堆
阿新 • • 發佈:2018-12-22
哈夫曼樹是一種二叉樹,但是它是一種加權路徑長度最短的二叉樹。
其可用堆來實現它的構建
其構建方法如下:
//heap.h
//仿函式
template<class T>
struct Less
{
bool operator()(const T& left, const T& right)const
{
return left < right;
}
};
template<class T>
struct Great
{
bool operator()(const T& left, const T& right)const
{
return left > right;
}
};
//給模板引數
template<class T,class Compare = Great<T>>
//先實現大堆 後面仿函式再實現小堆
class Heap
{
public:
//建構函式 無參+有參
//初始化空堆
Heap()
{}
//把陣列調成堆
Heap(T* a,size_t n)
{
//開空間 不把size增容 且對於空間不初始化
_a.reserve(n);
for (size_t i = 0; i < n; ++i)
{
//把數組裡的數字放在vector裡
_a.push_back(a[i]);
}
//建堆 構建成一個大堆 從最後一個非葉結點的父親開始
for (int i = (_a.size() - 2) / 2; i >= 0; --i)
{
//向下調整演算法 調成大堆
AdjustDown(i);
}
}
//即插入資料 從最後一個插入之後進行比較
void Push(const T& x)
{
_a.push_back(x);
AdjustUp(_a.size()-1);
}
void Pop()
{
//把最後一個節點和第一個節點進行交換 然後再進行向下調整
swap(_a[0], _a[_a.size() - 1]);
_a.pop_back();
AdjustDown(0);
}
//向下調整法 條件: 左右子樹必須是大堆
void AdjustDown(int root)
{
Compare com;
int parent = root;
int child = (parent * 2) + 1;
//停止的條件是孩子存在 即繼續的條件是孩子存在
while (child<_a.size())
{
//找左孩子右孩子大的那個 假設右孩子大於左孩子 右孩子可能不存在 需要一個判斷
//if (child+1<_a.size()&&_a[child+1] > _a[child])
if (child + 1<_a.size() && com(_a[child + 1] , _a[child]))
{
child++;
}
//走到這 就指向大的那個孩子 如果孩子大於父親節點 說明不滿足大堆 即交換 交換完之後父親和孩子節點要交換
//if (_a[child]> _a[parent])
if (com(_a[child], _a[parent]))
{
swap(_a[parent], _a[child]);
parent = child;
child = parent * 2 + 1;
}
//如果父親節點大於大的那個孩子 說明父親節點已經是最大的 不需要交換 即退出迴圈
else
{
break;
}
}
}
//向上調整法從最後一個節點開始 向上調整
void AdjustUp(int child)
{
//定義一個型別的物件
Compare com;
int parent = (child - 1) / 2;
while (child > 0)
{
//if (_a[child] >_a[parent])
if (com(_a[child], _a[parent]))
{
swap(_a[parent], _a[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
bool Empty()
{
return _a.empty();
}
size_t Size()
{
return _a.size();
}
T& Top()
{
//判斷size是否大於0 如果不大於0則沒有資料那麼就取不了堆頂
assert(_a.size() > 0);
return _a[0];
}
//判斷是否是堆 如果是大堆 父親節點大於孩子節點
bool IsHeap()
{
//可以用遞迴實現
//return _IsHeap();
//遍歷 判斷父親節點是否大於孩子節點
for (size_t i = 0; i < (_a.size() - 2) / 2; ++i)
{
if (_a[i] < _a[i * 2 + 1] || ((i*2+2)<_a.size()&&_a[i] < _a[i * 2 + 2]))
return false;
}
return true;
}
//建立小堆
void AdjustDown(int* heap, size_t k, size_t root)
{
assert(heap);
int parent = root;
int child = parent * 2 + 1;
while (child < k)
{
if (heap[child + 1] < heap[child])
++child;
if (heap[parent]>heap[child])
{
swap(heap[parent], heap[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
protected:
vector<T> _a;
};
#include"heap.h"
//加權路徑最優二叉樹
template<class W>
struct HuffmanNode
{
HuffmanNode<W>* _left;
HuffmanNode<W>* _right;
HuffmanNode<W>* _parent;
W _w;//權值
HuffmanNode(const W& w) :_w(w), _left(NULL), _right(NULL),_parent(NULL)
{}
};
template<class W>
class HuffmanTree
{
typedef HuffmanNode<W> Node;
public:
//建構函式
HuffmanTree() :root(NULL)
{}
HuffmanTree(W* a, size_t n,const W& invalid)
{
//有問題! 不能這麼構建 型別不能放權值 需要放節點
//Heap<W> minHeap(a, n);
/* W left;
W right;*/
//內部類 作用域受到類域的限制
struct NodeCompare
{
bool operator()(const Node* left, const Node* right)
{
return left->_w < right->_w;//調的過載的<
}
};
Heap<Node*,NodeCompare> minHeap;
for (size_t i = 0; i < n; ++i)
{
if (a[i] != invalid)
{
minHeap.Push(new Node(a[i]));
}
}
//如何選出節點最小的兩個節點??
//每次選權值最小的兩個節點其之和構建父親
while (minHeap.Size()>1){
Node* left = minHeap.Top();
minHeap.Pop();
Node* right = minHeap.Top();
minHeap.Pop();
//count相加
Node* parent = new Node(left->_w + right->_w);//父節點是左右孩子權值相加
parent->_left = left;
parent->_right = right;
left->_parent = parent;
right->_parent = parent;
minHeap.Push(parent);
}
_root = minHeap.Top();
}
Node* GetRoot()
{
return _root;
}
protected:
Node* _root;
};
//思考給的值為什麼都在葉子節點??
void testHuffmantree()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8 ,-1};
HuffmanTree<int> t1(a,sizeof(a)/sizeof(a[0]),-1);
}
如有不妥,歡迎指正。