哈夫曼树及哈夫曼编码(附代码)

简单介绍一下哈夫曼树的构造方法以及如何获取哈夫曼编码。

1.哈夫曼树的介绍

哈夫曼树又称最优树,是一类带权路径长度最短的树,在了解哈夫曼树之前首先需要了解路径,路径带权长度,权等相关概念。

(1)路径:从树中一个结点到另一个结点之间的分支构成两点之间的路径。

(2)路径长度:路径上分支数目称为路径长度。

(3)树的路径长度:从树根到每一个结点的路径长度之和。

(4)权值:即给树的某一结点或者边赋值

(5)结点的带权路径长度即从该节点树根之间的路径长度与结点权值的乘积;树的带权路径长度则是所有叶子节点的带权路径长度之和。

哈夫曼树即假设有m个权值{w1,w2···wn},可以构造一个含n个叶子结点的二叉树,每个结点的权值为wi,其中带权路径长度最小的二叉树即为哈夫曼树。

2.哈夫曼树的构造

哈夫曼树的构造过程:给定m个权值,假设有一个森林,森林里每棵树都是一个结点,每个结点的权值为wi,在森林中选取两个权值最小的结点构成一个新的二叉树,二叉树的左右孩子分别是所选取根节点的权值,再将两个结点从森林中删除,将新生成的二叉树加入森林当中,之后重复这个动作,直到森林当中只剩下一个结点,将该节点作为二叉树的根节点。

2.1哈夫曼树的存储结构

采用顺序表的结构进行存储。

class TreeNode{
public:
    int weight;//权值
    int parent,lchild,rchild;//该结点的双亲节点位置,左孩子位置,右孩子位置
};
2.2哈夫曼树的构造方法

动态申请2n个单元,然后先循环n次,输入叶子节点的权值,并将这n个结点的双亲结点,左右孩子结点都初始化为0;然后再循环n-1次,将剩下的结点的双亲和左右孩子结点初始化为0。

最后循环n-1次,通过n-1次循环,求出剩余节点的权值,双亲结点位置以及左右孩子位置。

void select(TreeNode **HT,int n,int &s1,int &s2){//选取数组中权值最小的那两项
    int min = MAXWEIGHT,nextmin = MAXWEIGHT;
    for(int i = 1; i <= n; i++){
        if((*HT)[i].parent == 0 && min >= (*HT)[i].weight ){
            s1 = i;
            min = (*HT)[i].weight;
        }
    }
    for(int i = 1; i <= n; i++){
        if((*HT)[i].parent == 0 && i != s1 && nextmin >= (*HT)[i].weight){
            s2 = i;
            nextmin = (*HT)[i].weight;
        }
    }
    (*HT)[s1].parent = n + 1;
    (*HT)[s2].parent = n + 1;
}

void CreateHafumanTree(TreeNode **HT,int n){//创建哈夫曼树
    if(n <= 1) cout<<"输入结点太少。"<<endl;
    int m = 2*n-1;
    (*HT) = new TreeNode[m+1];
    for(int i = 1; i <= n; i++){
        cout<<"请输入第"<<i<<"位置权值:"<<endl;
        cin>>(*HT)[i].weight;
        (*HT)[i].parent = 0;
        (*HT)[i].lchild = 0;
        (*HT)[i].rchild = 0;
    }
    for(int i = n; i <= m; i++){
        (*HT)[i].parent = 0;
        (*HT)[i].lchild = 0;
        (*HT)[i].rchild = 0;
    }
    int s1 = 1,s2 = 2;
    for(int i = n+1; i <= m;i++){//循环求出剩余结点的权值,双亲结点位置,以及左右孩子位置
        select(&(*HT),i-1,s1,s2);
        (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
        (*HT)[i].lchild = s1;
        (*HT)[i].rchild = s2;
    }
}
2.3哈夫曼编码

哈夫曼编码是从哈夫曼树的根节点出发,往左走为0,往右走为1,求出各个叶子结点的前缀编码。

我们可以从叶子结点出发,求得每个叶子结点的前缀编码的逆序,再将其翻转即的各个叶子结点的哈夫曼编码。

哈夫曼编码的存储结构

class Code{
public:
    string code;//哈夫曼编码
    int n,weight;//结点在哈夫曼表中位置和结点的权值
};

获取哈夫曼编码算法实现

void GetCode(TreeNode **HT,int n,Code **CodeForm){//获取哈夫曼编码
    for(int i = 1; i <= n; i++ ){
        string s = "";
        int station = i;
        (*CodeForm)[i].n = i;
        (*CodeForm)[i].weight = (*HT)[i].weight;
        int p = (*HT)[i].parent;
        while ( p!=0 )
        {
            if((*HT)[p].lchild == station) {
                s= "0" + s;
                station = p;
                p = (*HT)[p].parent; 
            }
            else if((*HT)[p].rchild == station){
                s = "1" + s;
                station = p;
                p = (*HT)[p].parent;
            }
        }
        cout<<"权值为"<<(*HT)[i].weight<<"的哈夫曼编码为:"<<s<<endl;
        (*CodeForm)[i].code = s;
    }
    
}

代码示例:

#include<iostream>
#include<stdio.h>

using namespace std;
#define MAXWEIGHT 10000
class TreeNode{
public:
    int weight;
    int parent,lchild,rchild;
};
class Code{
public:
    string code;
    int n,weight;
};
void select(TreeNode **HT,int n,int &s1,int &s2){//选取数组中权值最小的那两项
    int min = MAXWEIGHT,nextmin = MAXWEIGHT;
    for(int i = 1; i <= n; i++){
        if((*HT)[i].parent == 0 && min >= (*HT)[i].weight ){
            s1 = i;
            min = (*HT)[i].weight;
        }
    }
    for(int i = 1; i <= n; i++){
        if((*HT)[i].parent == 0 && i != s1 && nextmin >= (*HT)[i].weight){
            s2 = i;
            nextmin = (*HT)[i].weight;
        }
    }
    (*HT)[s1].parent = n + 1;
    (*HT)[s2].parent = n + 1;
}

void CreateHafumanTree(TreeNode **HT,int n){//创建哈夫曼树
    if(n <= 1) cout<<"输入结点太少。"<<endl;
    int m = 2*n-1;
    (*HT) = new TreeNode[m+1];
    for(int i = 1; i <= n; i++){
        cout<<"请输入第"<<i<<"位置权值:"<<endl;
        cin>>(*HT)[i].weight;
        (*HT)[i].parent = 0;
        (*HT)[i].lchild = 0;
        (*HT)[i].rchild = 0;
    }
    for(int i = n; i <= m; i++){
        (*HT)[i].parent = 0;
        (*HT)[i].lchild = 0;
        (*HT)[i].rchild = 0;
    }
    int s1 = 1,s2 = 2;
    for(int i = n+1; i <= m;i++){//循环求出剩余结点的权值,双亲结点位置,以及左右孩子位置
        select(&(*HT),i-1,s1,s2);
        (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
        (*HT)[i].lchild = s1;
        (*HT)[i].rchild = s2;
    }
}
void GetCode(TreeNode **HT,int n,Code **CodeForm){//获取哈夫曼编码
    for(int i = 1; i <= n; i++ ){
        string s = "";
        int station = i;
        (*CodeForm)[i].n = i;
        (*CodeForm)[i].weight = (*HT)[i].weight;
        int p = (*HT)[i].parent;
        while ( p!=0 )
        {
            //cout<<"station:"<<station<<endl;
            if((*HT)[p].lchild == station) {
                s= "0" + s;
                station = p;
                p = (*HT)[p].parent; 
            }
            else if((*HT)[p].rchild == station){
                s = "1" + s;
                station = p;
                p = (*HT)[p].parent;
            }
        }
        cout<<"权值为"<<(*HT)[i].weight<<"的哈夫曼编码为:"<<s<<endl;
        (*CodeForm)[i].code = s;
    }
    
}


int main(){
    int n;
    TreeNode *HT;
    cout<<"请输入结点数:"<<endl;
    cin>>n;
    Code *CodeForm = new Code[n+1];
    CreateHafumanTree(&HT,n);
    for(int i = 1; i <= n*2 - 1; i++){
        cout<<i<<"|   "<<HT[i].weight<<"|   "<<HT[i].parent<<"|   "<<HT[i].lchild<<"|   "<<HT[i].rchild<<endl;
    }
    GetCode(&HT,n,&CodeForm);
    return 0;
}

最后是运行结果图:

如有错误,欢迎指出评论。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
哈夫曼树是一种用于数据压缩的树形结构,它可以根据数据的出现频率来构建对应的编码表,从而实现数据的高效压缩。下面是哈夫曼树编码算法的代码实现: ```python class Node: def __init__(self, freq, char=None): self.freq = freq self.char = char self.left = None self.right = None def build_huffman_tree(s): freq_dict = {} for char in s: if char not in freq_dict: freq_dict[char] = 0 freq_dict[char] += 1 nodes = [] for char, freq in freq_dict.items(): nodes.append(Node(freq, char)) while len(nodes) > 1: nodes = sorted(nodes, key=lambda x: x.freq) left = nodes[0] right = nodes[1] parent = Node(left.freq + right.freq) parent.left = left parent.right = right nodes = nodes[2:] nodes.append(parent) return nodes[0] def get_codes(root, code, codes_dict): if root.char: codes_dict[root.char] = code return get_codes(root.left, code + '0', codes_dict) get_codes(root.right, code + '1', codes_dict) def huffman_encoding(s): if not s: return '', None root = build_huffman_tree(s) codes_dict = {} get_codes(root, '', codes_dict) encoded = ''.join([codes_dict[char] for char in s]) return encoded, root def huffman_decoding(encoded, root): if not encoded or not root: return '' decoded = '' node = root for bit in encoded: if bit == '0': node = node.left else: node = node.right if node.char: decoded += node.char node = root return decoded ``` 其中,`build_huffman_tree` 函数用于构建哈夫曼树,`get_codes` 函数用于获取字符对应的编码,`huffman_encoding` 函数用于对字符串进行编码,`huffman_decoding` 函数用于对编码后的字符串进行解码。 下面是一个简单的使用示例: ```python s = "hello world" encoded, root = huffman_encoding(s) print(encoded) # 输出:'011111101101010000100111111110110110011000111001' decoded = huffman_decoding(encoded, root) print(decoded) # 输出:'hello world' ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值