哈夫曼树的建立

1、基本概念来自:http://blog.csdn.net/wtfmonking/article/details/17150499#


a、路径和路径长度

若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 kiki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径。

从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1.


b、结点的权和带权路径长度

在许多应用中,常常将树中的结点赋予一个有着某种意义的实数,我们称此实数为该结点的权,(如下面一个树中的蓝色数字表示结点的权)

结点的带权路径长度规定为从树根结点到该结点之间的路径长度与该结点上权的乘积。


c、树的带权路径长度

树的带权路径长度定义为树中所有叶子结点的带权路径长度之和,公式为:


其中,n表示叶子结点的数目,wi 和 li 分别表示叶子结点 ki 的权值和树根结点到 ki 之间的路径长度。

如下图中树的带权路径长度 WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4  =  122



d、哈夫曼树

哈夫曼树又称最优二叉树。它是 n 个带权叶子结点构成的所有二叉树中,带权路径长度 WPL 最小的二叉树。

如下图为一哈夫曼树示意图。


2、构造哈夫曼树


假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:


(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);


(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;


(3)从森林中删除选取的两棵树,并将新树加入森林;


(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。


 如:对 下图中的六个带权叶子结点来构造一棵哈夫曼树,步骤如下:


注意:为了使得到的哈夫曼树的结构尽量唯一,通常规定生成的哈夫曼树中每个结点的左子树根结点的权小于等于右子树根结点的权。

实现代码(vector):

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
#include<math.h>
#include<stdlib.h>
#include<queue>
#include<map>
#include<set>
#define bug printf("*********\n");
#define mem0(a) memset(a, 0, sizeof(a));
#define mem1(a) memset(a, -1, sizeof(a));
#define in1(a) scanf("%d" ,&a);
#define in2(a, b) scanf("%d%d", &a, &b);
#define out1(a) printf("%d\n", a);
#define out2(a, b) printf("%d %d\n", a, b);
using namespace std;
typedef long long LL;
typedef pair<int, int> par;
const int mod = 1e9+7;
const int INF = 1e9+7;
const int N = 1000010;
const double pi = 3.1415926;

int n, a[100], ans;

struct node
{
    int data;
    int num[100]; //编码
    node *l;
    node *r;
};

vector<node*> V; //我们用vector来装节点
vector<node*>::iterator it;

bool cmp(node *a, node *b)
{
    return a->data < b->data;
}

node* build() //建树函数,返回根节点
{
    while(V.size() > 1) {
        node *p, *p1, *p2; //p1为最小值,跑为次小值
        it = V.begin();
        p1 = *it;
        V.erase(it);
        it;
        p2 = *it;
        V.erase(it);
        p = new node; //为子节点设置根节点
        p->data = p1->data+p2->data;
        p->l = p1;
        p->r = p2;
        V.push_back(p);
        sort(V.begin(), V.end(), cmp); //每次排序
    }
    it = V.begin();
    return *it;
}

void dfs(node *t, int l) //计算带权路径长度
{
    if(t->l == NULL && t->r == NULL) {
        ans += t->data*l;
        return;
    }
    dfs(t->l, l+1);
    dfs(t->r, l+1);
}

int main()
{
    while(~scanf("%d", &n)) {
        V.clear();
        ans = 0;
        for(int i = 0; i < n; i ++) { //初始化叶子节点
            in1(a[i]);
            node *p;
            p = new node;
            p->data = a[i];
            p->l = NULL;
            p->r = NULL;
            V.push_back(p);
        }
        sort(V.begin(), V.end(), cmp);
        node *tree;
        tree = build();
        dfs(tree, 0);
        printf("\n带权路径长度:");
        printf(" %d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/i_believe_cwj/article/details/80412467