huffman樹,又稱最優二叉樹,是加權路徑長度最短的二叉樹
[貪心算法]是指在問題求解時,總是做出當前看起來最好的選擇,通俗的說貪心算法不是整體最優的選擇,而是局部最優解
使用貪心算法構建Huffman樹
構建哈夫曼樹主要包括兩部分:
1:建小堆
2:將加權路徑長度最短,構建最短二叉樹
一:建小堆:
#pragma once
#include<vector>
#include<assert.h>
using namespace std;
template<class T>
class Less
{
public:
bool operator()(const T&a, const T&b)
{
return a < b;
}
};
template<class T>
class Greater
{
public:
bool operator()(const T&a, const T&b)
{
return a>b;
}
};
//小堆:任意一個節點是它子樹的最大節點
template<class T, class Compare = Less<T>>
class Heap
{
public:
Heap()
{}
Heap(T*a, size_t n)
{
_a.reserve(n);//增容(可以直接拷貝數據)
for (size_t i = 0; i < n; i++)
{
_a.push_back(a[i]);
}
//調整成堆
for (int j = (_a.size() - 2) / 2; j >= 0; --j)
{
//向下調整
_AjustDown(j);
}
}
//插入
void Push(const T&x)
{
_a.push_back(x);
//調整成堆
_AjustUp(_a.size() - 1);
}
//刪除
void Pop()
{
assert(!_a.empty());
swap(_a[0], _a[_a.size() - 1]);//根節點和最後一個葉子節點交換
_a.pop_back();
_AjustDown(0);
}
const T& Top()
{
return _a[0];
}
size_t Size()
{
return _a.size();
}
bool Empty()
{
return _a.empty();
}
protected:
//插入算法,主要影響root路徑,向上
void _AjustUp(int child)
{
assert(!_a.empty());
int parent = (child - 1) >> 1;
Compare comFunc;//調用仿函數
while (child>0)
{
//如果孩子節點的值大於父節點的值
if (comFunc(_a[child], _a[parent]))
{
swap(_a[child], _a[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
{
break;
}
}
}
protected:
//向下調整成堆
void _AjustDown(int root)
{
Compare comFuc;
int parent = root;
int child = root * 2 + 1;//左孩子
while (child < _a.size())
{
//選較小的孩子
if (child + 1 < _a.size() && comFuc(_a[child + 1], _a[child]))
{
++child;
}
//比較父親與孩子的大小
if (comFuc(_a[child], _a[parent]))
{
swap(_a[child], _a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
protected:
vector<T>_a;
};
void TestHeap()
{
int a1[] = { 10, 16, 18, 12, 13, 15, 17, 14, 19 };
size_t len = sizeof(a1) / sizeof(a1[0]);
Heap<int, Less<int>> h1(a1, len);
h1.Top();
/*h1.Push(25);
h1.Pop();*/
while (!h1.Empty())
{
cout << h1.Top() << " ";
h1.Pop();
}
cout << endl;
}
二:構建哈夫曼樹:
構建哈夫曼樹,是所有葉子節點帶權路徑之和.
建樹的思路:
1:將所有節點看成獨立的樹,且左右子樹都爲空,
2:選擇權值最小的兩個節點,生成一個節點作爲它們的父節點,父節點的權值等於它們的權值之和
3:將父節點放回重複步驟2直到這個序列中只剩下最後一個節點,此時哈夫曼樹就建成了.
#pragma once
#include"Heap.h"
#include<assert.h>
using namespace std;
template<class T>
struct HuffmanTresNode
{
T _w;//權值
HuffmanTresNode<T> *_left;
HuffmanTresNode<T> *_right;
HuffmanTresNode<T> *_parent;
HuffmanTresNode(const T&x = T())
:_w(x)
, _left(NULL)
, _right(NULL)
, _parent(NULL)
{}
};
template<class T>
class HuffmanTree
{
typedef HuffmanTresNode<T> Node;
public:
//缺省的構造函數
HuffmanTree()
:_root(NULL)
{}
HuffmanTree(T*a, size_t n, const T& invalid = T())//創建huffmanTree
{
assert(a);
//_root = CreatHuffmanTree(a, n, invalid);
//比較權值
struct Compare
{
bool operator()(const Node*l, const Node*r) const
{
return l->_w < r->_w;
}
};
Heap<Node*, Compare> minHeap;//所有元素建小堆
for (size_t i = 0; i < n; ++i)
{
if (a[i] != invalid)
{
minHeap.Push(new Node(a[i]));
}
}
Node*left = NULL;
Node*right = NULL;
while (minHeap.Size()>1)
{
left = minHeap.Top();
minHeap.Pop();
right = minHeap.Top();
minHeap.Pop();
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* GetHuffmanTree()
{
return _root;
}
~HuffmanTree()
{
if (_root != NULL)
_Destroy(_root);
}
protected:
//釋放空間9
void _Destroy(Node*node)
{
if (node == NULL)
return;
_Destroy(node->_left);
_Destroy(node->_right);
delete node;
}
protected:
Node* _root;
};
void TestHuffmanTree()
{
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
HuffmanTree<int> t(a, 10);
}
當然哈夫曼樹的一個重要應用就是通過哈夫曼編碼來實現文件壓縮,關於文件壓縮後面我們再來探討.