哈夫曼树及哈夫曼编码构建的代码实现

哈夫曼树构建的核心思路是在创建每一个结点时给结点赋权值,同时推该结点进入“优先队列”。根据优先队列的特性,取优先队列的俩顶部结点生成新结点(俩顶部结点权值相加,并作为新结点的左右子结点),俩顶部结点出队,新结点入队。若干次循环后,队列只剩下一个结点,该节点便是哈夫曼树的根节点。

哈夫曼编码的生成见代码,递归简单易懂。

树与编码构建代码如下:

#include<iostream>
#include<queue>
#include<stack>
#include<vector>
#include<string>
#include<algorithm>
#include<functional>
using namespace std;

//创建一个全局字符数组
char Arr[10000];

//创建一个哈夫曼树的类
class HuffmanTreeNode{
private:
    //weight是权值,Huffmancode用来记录生成的哈夫曼编码
    int weight;
    string Huffmancode;
    HuffmanTreeNode *lchild;
    HuffmanTreeNode *rchild;
public:
    //
    void Set(int num, HuffmanTreeNode *left, HuffmanTreeNode *right) {
        this->weight = num;
        this->lchild = left;
        this->rchild = right;
    }

    int getWeight() {
        return this->weight;
    }

    //判断这个结点是否为叶子结点,是的话返回true
    bool isLeaf(HuffmanTreeNode *root) {
        return !root->lchild && !root->rchild;
    }

    //哈夫曼编码生成,top是数组的下标,没操作一次就往后走一位
    void Create_HuffmanCode(HuffmanTreeNode *root, char *Arr, int top) {
        //根的左结点不为空时进行操作
        if (root->lchild) {
            Arr[top] = '0';
            Create_HuffmanCode(root->lchild, Arr, top + 1);
        }
        //根的右结点不为空时进行操作
        if (root->rchild) {
            Arr[top] = '1';
            Create_HuffmanCode(root->rchild, Arr, top + 1);
        }
        //该结点是叶子节点时进行操作
        if (this->isLeaf(root)) {
            for (int i = 0; i < top; i++) {
                if (Arr[i-1]==' '&&Arr[i]!=' ') {
                    break;
                }
            }
            //arr是字符数组,top是arr中的任意长度(整型)
            string code(Arr, top);
            int index;
            //如果字符串不存在包含关系,那么返回值就一定是npos
            //这里的code.find()赋值给index后,黄色括号返回的值就是find返回的值吗
            while ((index=code.find(' '))!=code.npos) {
                code=code.erase(index, 1);
            }
            root->Huffmancode=code;
        }
    }

    void PrintHuffmanTree(HuffmanTreeNode *root) {
        queue<HuffmanTreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            HuffmanTreeNode *cur = q.front();
            q.pop();
            if (cur->lchild) {
                q.push(cur->lchild);
            }
            if (cur->rchild) {
                q.push(cur->rchild);
            }
            if (this->isLeaf(cur)) {
                cout<<cur->weight<<"的哈夫曼编码为:"<<cur->Huffmancode<<endl;
            }
        }
    }

    //递归前序遍历
    void PreorderTraversal(HuffmanTreeNode *T) {
        if (T == NULL) {
            return;
        }
        //访问根节点并输出
        cout << T->weight << " ";
        T->PreorderTraversal(T->lchild);
        T->PreorderTraversal(T->rchild);
    }

    //递归中序遍历
    void InorderTraversal(HuffmanTreeNode *T) {
        if (T == NULL) {
            return;
        }
        T->InorderTraversal(T->lchild);
        cout << T->weight << " ";
        T->InorderTraversal(T->rchild);
    }

    //递归后序遍历
    void PostorderTraversal(HuffmanTreeNode *T) {
        if (T == NULL) {
            return;
        }
        T->PostorderTraversal(T->lchild);
        T->PostorderTraversal(T->rchild);
        cout << T->weight << " ";
    }
};

struct cmp{
    bool operator()(HuffmanTreeNode *node1, HuffmanTreeNode *node2) {
        return node1->getWeight() > node2->getWeight();
    }
};

HuffmanTreeNode *Create_HuffmanTree(int *data, int size) {
    //这里的cmp是小顶栈
    priority_queue<HuffmanTreeNode*, vector<HuffmanTreeNode *>, cmp> Q;
    //给size个结点赋权值,并让他们入队列
    for (int i = 0; i < size; i++) {
        HuffmanTreeNode *root = new HuffmanTreeNode;
        root->Set(data[i], NULL, NULL);
        Q.push(root);
    }
    //注意Q是永远不为空的,最少的时候是一个结点
    while (Q.size()!=1) {
        HuffmanTreeNode *left = Q.top();
        Q.pop();
        HuffmanTreeNode *right = Q.top();
        Q.pop();
        HuffmanTreeNode *root = new HuffmanTreeNode;
        //这个Set是关键一步,队列前两个结点权值相加,将新权值赋给新结点,然后设置新结点的左右结点
        root->Set(left->getWeight() + right->getWeight(), left, right);
        //将新结点入队,这里就体现出了优先队列的好处了,在队列里,不管先后插入顺序,先输出的必是最小的一个
        Q.push(root);
    }
    //拿到顶部的根!
    HuffmanTreeNode *root=Q.top();
    Q.pop();
    return root;
}

int main(){
    int N,*data;
    cout<<"请输入哈夫曼树初始结点数:"<<endl;
    cin>>N;
    data=new int[N];
    for(int i=0; i<N; i++) {
        cin>>data[i];
    }
    HuffmanTreeNode *root;
    root=Create_HuffmanTree(data, N);
    root->Create_HuffmanCode(root, Arr, N);
    root->PrintHuffmanTree(root);

    cout<<"前序遍历(递归):"<<endl;
    root->PreorderTraversal(root);
    cout<<endl;

    cout << "中序遍历(递归):" << endl;
    root->InorderTraversal(root);
    cout << endl;

    cout << "后序遍历(递归):" << endl;
    root->PostorderTraversal(root);
    cout << endl;

    return 0;
}

输入:

请输入哈夫曼树初始结点数:
5
1 2 3 4 5

输出:

3的哈夫曼编码为:     00
4的哈夫曼编码为:     10
5的哈夫曼编码为:     11
1的哈夫曼编码为:     010
2的哈夫曼编码为:     011
前序遍历(递归):
15 6 3 3 1 2 9 4 5
中序遍历(递归):
3 6 1 3 2 15 4 9 5
后序遍历(递归):
3 1 2 3 6 4 5 9 15
 

over!

-------------------------------------------2021年10月29日更新-------------------------------------------

深圳大学oj题目——赫夫曼树的构建与编码

代码如下:

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

const int MaxW=9999;

//定义HuffMan树结点类
class HuffNode{
public:
    int weight; //权值
    int parent; //父结点下标
    int leftchild; //左孩子下标
    int rightchild; //右孩子下标
};

//定义哈夫曼树
class HuffMan{
private:
    void MakeTree();
    //从1到pos的位置找出权值最小的两个结点,把结点下标存在s1和s2中
    void SelectMin(int pos,int *s1,int *s2);

public:
    int len;  //结点数量
    int lnum;   //叶子数量
    HuffNode* huffTreeRoots; //哈夫曼树,用数组表示
    string *huffCode;   //每个字符对应的哈夫曼编码
    void MakeTree(int n,int wt[]);
    void Coding();
    void Destroy();
};

//构建HuffMan树
void HuffMan::MakeTree(int n, int wt[]) { //参数是叶子结点数量和叶子权值
    int i;
    lnum=n;
    len=2*n-1;
    //位置从1开始计算
    huffTreeRoots=new HuffNode[2*n];
    huffCode=new string[lnum+1];

    //哈夫曼树huffmanTree初始化
    for(i=1;i<=n;i++)
        huffTreeRoots[i].weight=wt[i-1];//第0号不用,从1开始编号
    for(i=1;i<=len;i++){
        //前n个是叶子结点,已经设值
        if(i>n)
            huffTreeRoots[i].weight=0;//
        huffTreeRoots[i].parent=0;
        huffTreeRoots[i].leftchild=0;
        huffTreeRoots[i].rightchild=0;
    }
    MakeTree();
}

void HuffMan::SelectMin(int pos, int *s1, int *s2) {
    //第i结点的权值和下标保存到w1和s1,作为第一最小值
    //第i结点的权值和下标保存到w2和s2,作为第二最小值
    int w1,w2,i;
    w1=w2=MaxW;
    //s1与s2是俩最小权值的下标
    *s1=*s2=0;
    for(i=1;i<=pos;i++){
        if(huffTreeRoots[i].weight<w1&&huffTreeRoots[i].parent==0){
            w2=w1;
            *s2=*s1;
            *s1=i;
            w1=huffTreeRoots[i].weight;
        }else if(huffTreeRoots[i].weight<w2&&huffTreeRoots[i].parent==0){
            *s2=i;
            w2=huffTreeRoots[i].weight;
        }
    }
}

void HuffMan::MakeTree() {
    int i,s1,s2;
    //构造哈夫曼树的n-1个非叶结点
    for(i=lnum+1;i<=len;i++){
        //想象从左往右有一个数组
        SelectMin(i-1,&s1,&s2);
        huffTreeRoots[s1].parent=i;
        huffTreeRoots[s2].parent=i;
        huffTreeRoots[i].leftchild=s1;
        huffTreeRoots[i].rightchild=s2;
        huffTreeRoots[i].weight=huffTreeRoots[s1].weight+huffTreeRoots[s2].weight;
    }
}

//销毁哈夫曼树
void HuffMan::Destroy() {
    len=0;
    lnum=0;
    delete[] huffTreeRoots;
    delete[] huffCode;
}

//哈夫曼编码
void HuffMan::Coding() {
    char *cd;
    int i,c,f,start;

    //求n个叶结点的哈夫曼编码
    cd=new char[lnum];  //分配求编码的工作空间
    cd[lnum-1]='\0';      //编码结束符
    for(i=1;i<=lnum;++i){ //逐个字符求哈夫曼编码
        start=lnum-1;  //编码结束符位置
        for(c=i,f=huffTreeRoots[i].parent;f!=0;c=f,f=huffTreeRoots[f].parent)
        {
            if(huffTreeRoots[f].leftchild==c){
                start--;
                cd[start]='0';
            }else{
                start--;
                cd[start]='1';
            }
        }
        //把cd中从start到末尾的编码复制到huffCode中
        huffCode[i].assign(&cd[start]);
    }
    delete[]cd; //释放工作空间
}

int main(){
    int t,n,i,j;
    int wt[800];
    HuffMan myHuff;
    cin>>t;
    for(i=0;i<t;i++){
        cin>>n;
        for(j=0;j<n;j++)
            cin>>wt[j];
        myHuff.MakeTree(n,wt);
        myHuff.Coding();
        for(j=1;j<=n;j++){
            //输出各权值
            cout<<myHuff.huffTreeRoots[j].weight<<'-';
            //输出各编码
            cout<<myHuff.huffCode[j]<<endl;
        }
        myHuff.Destroy();
    }
    return 0;
}

我是花花,祝自己也祝您变强了~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕飞雨的头发不能秃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值