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);
}
当然哈夫曼树的一个重要应用就是通过哈夫曼编码来实现文件压缩,关于文件压缩后面我们再来探讨.