我要写的这篇内容主要是哈夫曼树的定义和建立过程,另外还包括了使用前序,中序,后序遍历来输出构建好的哈夫曼树,以验证哈夫曼树是否建立成功。
哈夫曼树定义
哈夫曼树就是带权外部路径长度最小的二叉树,是不是不太好理解?其实说白了,就是叶子节点的权值与它到根节点的路径长度加权和最小的一棵二叉树。假设有四个外部节点为2,3,4,6,则它的二叉树组合如下所示:
当然,有很多组合,我就不一一列举了,我就以上图为例计算带权外部路径长度。2,3,4,6为叶子节点的权值,权值为2的叶子节点到根节点的路径长度为2,很明显,其他几个节点到根节点的路径长度也为2,所以这个带权外部路径长度的计算为:2*2+3*2+4*2+6*2=30,知道了这个哈夫曼树的定义之后,我们需要思考的是怎么使这个二叉树的外部路径长度最小呢?
既然是(权值×到根节点的路径),那么权值越大的叶子使它到根节点的路径尽量短,这样所得的二叉树的路径和应该就是最小的,好了,思路明白了,那么怎么转化成代码实现呢?
哈夫曼树的建立过程
- 首先初始化每个外部节点,把2,3,4,6转化为四颗树,加入一个集合中{tree2,tree3,tree4,tree6}
- 再把这些树按照从小到大排序,取出其中权值最小的两个节点,以这两个节点分别为左子树和右子树,为它们添加父节点,父节点的权值为这两个子节点的权值和,把这个父节点加入到树的集合中,因为我们取出的节点为tree2,tree3,所以这个加入到集合中的节点为tree5,此时集合中共有三个节点,分别为{ tree4,tree5,tree6 }。
- 重复操作,先排序,再取出最小的两个节点,再添加完父节点加入到集合中。此时取出的节点为tree4和tree5,加入的节点为tree9,集合中还剩两个节点,集合为{ tree6,tree9 }。
- 再重复步骤,取出的两个节点为tree6和tree9,加入的节点为tree15,因为此时集合中已经只剩一个节点,所以哈夫曼树构建完成,结束循环。
处理完的哈夫曼树如下图所示:
你可以尝试计算一下,它的外部路径长度,为2×3+3×3+4×2+6×1=29,是最小的,因此这个就是我们构建的哈夫曼树。
代码如下所示:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
class Node
{
public:
int weight;
bool label;
Node *leftChild;
Node *rightChild;
Node(int val,bool lab,Node *left=NULL,Node *right=NULL)
{
weight = val;
label = lab;
leftChild = left;
rightChild = right;
}
};
bool compare(Node *a,Node *b)
{
return a->weight > b->weight;
}
Node * BuildHuffumanTree(vector<int> input) //建立哈夫曼树
{
vector<Node*> vecNode;
int size = input.size();
for (int i = 0; i < size; i++)
{
vecNode.push_back(new Node(input[i],true));
}
while (vecNode.size() != 1)
{
sort(vecNode.begin(),vecNode.end(),compare);
Node *node1 = vecNode[vecNode.size() - 1];
Node *node2 = vecNode[vecNode.size() - 2];
vecNode.pop_back();
vecNode.pop_back();
Node *node3 = new Node(node1->weight+node2->weight,false);
node3->leftChild = node2;
node3->rightChild = node1;
vecNode.push_back(node3);
}
return vecNode[0];
}
void preDFS(Node *root) //前序遍历
{
if (root == NULL)
{
return;
}
else
{
cout << root->weight << endl;
preDFS(root->leftChild);
preDFS(root->rightChild);
}
}
void midDFS(Node *root) //中序遍历
{
if (root == NULL)
{
return;
}
else
{
midDFS(root->leftChild);
cout << root->weight << endl;
midDFS(root->rightChild);
}
}
void backDFS(Node *root) //后序遍历
{
if (root == NULL)
{
return;
}
else
{
backDFS(root->leftChild);
backDFS(root->rightChild);
cout << root->weight << endl;
}
}
int main()
{
vector<int> input = { 2,3,4,6 };
Node *node= BuildHuffumanTree(input); //node即为根节点
cout << "前序遍历" << endl;
preDFS(node);
cout << endl;
cout << "中序遍历" << endl;
midDFS(node);
cout << endl;
cout << "后序遍历" << endl;
backDFS(node);
cout << endl;
system("pause");
return 0;
}
getMinPathLength返回的值就是最后一个剩下的节点地址,也就是根节点的地址,拿到这个地址后再对这个二叉树进行前序,中序,后序遍历,以验证程序运行的准确性。