其他分享
首页 > 其他分享> > Huffman Coding 哈夫曼树

Huffman Coding 哈夫曼树

作者:互联网

一、实验名称:Huffman Coding

二、实验目的:

  1. 熟练掌握哈夫曼树的数据结构,结构的特点;
  2. 能够实现哈夫曼树的基本操作:如构造,插入等
  3. 利用最小堆降低哈夫曼树的时间复杂度。
  4. 熟练掌握最小堆的数据结构,结构的特点;
  5. 能够实现最小堆的基本操作:如构造,插入,删除等

三、实验内容:

  1. Determine the data structures for a binary Huffman tree

    //采取树的结构构造哈夫曼树
    //采取数组的结构构造最小堆
    typedef struct TreeNode*Huffman;
    typedef  struct TreeNode** heap;
    struct TreeNode
    {
        int data;
        Huffman left;
        Huffman right;
    };
    
  2. Implement the algorithm of creating a binary Huffman tree for a given postive integer sequence

    输入一段序列,利用该序列数组构造最小堆,以便于哈夫曼树的构造。

    //利用序列构造最小堆
    void BuildMinHeap(heap h,int size);
    //利用最小堆构造哈夫曼树
    Huffman BuildHuffman(heap h,int size);
    
  3. Design and implement a Huffman coding prototype

    //采用深搜的方法打印所有的哈夫曼编码
    void getHuffmanCode(Huffman h,char* codeStore,int* index);
    

四. 程序清单(较为详细的注释):

#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode*Huffman;
typedef  struct TreeNode** heap;
struct TreeNode
{
    int data;
    Huffman left;
    Huffman right;
};
//向下过滤,在子树都为最小堆的前提下,找到节点的合适位置,是以该节点为根节点的树是一个最小堆
void FixDown(heap h,int size,int index){
    int parent;
    int child;
    Huffman temp=h[index];
    for(parent=index;parent*2<=size;parent=child){
        child=parent*2;
        if(child+1<=size&&h[child+1]->data<h[child]->data)
            child++;
        if(h[child]->data<temp->data){
            h[parent]=h[child];
        }
        else break;
    }
    h[parent]=temp;
}
//采用递归的思想,从下往上建立最小堆,保证了目前所建树的子树都为最小堆。
void BuildMinHeap(heap h,int size){
    for(int i=size/2;i>=1;i--){
        FixDown(h,size,i);
    }
}
//返回并删除最小堆的最小值(即头节点),保持树仍为最小堆
/*将最后一个节点挪动到头节点处,并size--,
因为此时子树均为最小堆,直接向下过滤即可保证该树仍为最小堆*/
Huffman PopMinHeap(heap h,int* size){
    Huffman result=h[1];
    h[1]=h[(*size)--];
    //向下过滤
    FixDown(h,*size,1);
    return result;
}
//插入节点值,保持树仍为最小堆
/**/
void InsertMinHeap(heap h,int* size,Huffman node){
    h[++(*size)]=node;
    //向上过滤
    int child=*size;
    int parent;
    for(;child>1;child=parent){
        parent=child/2;
        if(h[parent]->data>node->data){
            h[child]=h[parent];
        }
        else
        {
            break;
        }
        
    }
    h[child]=node;
}
//建立哈夫曼树
Huffman BuildHuffman(heap h,int size){
    Huffman node=(Huffman)malloc(sizeof(struct TreeNode));
    Huffman fristNode,lastNode;
    int time=size-1;
    for(int i=0;i<time;i++){//进行size-1次合并
        fristNode=PopMinHeap(h,&size);
        lastNode=PopMinHeap(h,&size);
        Huffman node=(Huffman)malloc(sizeof(struct TreeNode));
        node->data=fristNode->data+lastNode->data;
        node->left=fristNode;
        node->right=lastNode;
        InsertMinHeap(h,&size,node);
    }
    return PopMinHeap(h,&size);
    
}
//以“结点值(左子树)(右子树)“的形式打印树,证明它是哈夫曼树
void print(Huffman h){
    if(h){
        printf("%d(",h->data);
        print(h->left);
        printf(")(");
        print(h->right);
        printf(")");
    }
}
//释放哈弗曼树空间
void deleteHuff(Huffman h){
    if(h){
        deleteHuff(h->left);
        deleteHuff(h->right);
        free(h);
    }
}
//采用深搜的方法打印所有的哈夫曼编码
void getHuffmanCode(Huffman h,char* codeStore,int* index){
    if(!h)return;
    if(h->left==NULL&&h->right==NULL){
        printf("\n%d's HuffmanCode:",h->data);
        codeStore[*index]='\0';
        printf("%s",codeStore);
    }
    else{
        codeStore[(*index)++]='0';
        getHuffmanCode(h->left,codeStore,index);
        --(*index);//回溯
        codeStore[(*index)++]='1';
        getHuffmanCode(h->right,codeStore,index);
        --(*index);//回溯
    }
}
int main() {
    int n;//输入序列的个数;
    printf("Please enter the number of sequences:\n");
    scanf("%d",&n);
    heap MinHeap=(heap)malloc(sizeof(struct TreeNode*)*(2*n));//保证堆有足够的空间
    //建立最小堆
    printf("Please enter the sequence one by one:\n");
    for(int i=1;i<=n;i++){
        MinHeap[i]=(Huffman)malloc(sizeof(struct TreeNode));
        scanf("%d",&MinHeap[i]->data);
        MinHeap[i]->left=NULL;
        MinHeap[i]->right=NULL;
    }
    BuildMinHeap(MinHeap,n);
    // for(int i=1;i<=n;i++){
    //     printf("%d ",MinHeap[i]->data);
    // }
    //建立哈夫曼树
    Huffman huff=BuildHuffman(MinHeap,n);
    //打印哈夫曼树,格式为“父结点(左子树)(右子树)"以证明其为哈夫曼树
    printf("Print Huffman Tree\n");
    print(huff);
    //输出所有原输入数的哈夫曼编码
    printf("\nOutput the Huffman code of all original input numbers");
    char* codeStore=(char*)malloc(sizeof(char)*n);
    int index=0;
    getHuffmanCode(huff,codeStore,&index);
    free(codeStore);
    //内存释放工作
    free(MinHeap);
    deleteHuff(huff);
    return 0;
}#include <stdio.h>
#include <stdlib.h>
//在二叉树数据结构基础上添加高度成员.
struct ALVnode
{
    int val;
    struct ALVnode* left;
    struct ALVnode* right;
    int high;
};
typedef struct ALVnode* ALV;
int getHigh(ALV tree){
    if(!tree) return 0;
    int i=getHigh(tree->left);
    int j=getHigh(tree->right);
    return i>j?i+1:j+1;
}
/*将root与其左节点做左单旋(右转),因为root和其左节点的树高发生改变
,于是需要更新两者的高度,将根结点指向新的根节点(即左节点)并返回*/
ALV LeftLeft(ALV root){
    ALV temp=root;
    root=root->left;
    temp->left=root->right;
    root->right=temp;
    //更新树高
    temp->high=getHigh(temp);
    root->high=getHigh(root);
    return root;
}
/*将root与其右节点做右单旋(左转),因为root和其左节点的树高发生改变
,于是需要更新两者的高度,将根结点指向新的根节点(即右节点)并返回*/
ALV RightRight(ALV root){
    ALV temp=root;
    root=root->right;
    temp->right=root->left;
    root->left=temp;
    //更新树高
    temp->high=getHigh(temp);
    root->high=getHigh(root);
    return root;
}
/*将root的左子树进行右单旋可以使插入结点转移到左子树的左子树上,此时再进行root整体的左单旋*/
ALV LeftRight(ALV root){
    root->left=RightRight(root->left);
    root=LeftLeft(root);
    return root;
}
/*将root的右节点进行左单旋可以使插入结点转移到右子树的右子树上,此时再进行root整体的右单旋*/
ALV RightLeft(ALV root){
    root->right=LeftLeft(root->right);
    root=RightRight(root);
    return root;
}
ALV insert(ALV root,int val){
    if(root==NULL){//此时val是第一个数
        struct ALVnode*newNode=(struct ALVnode*)malloc(sizeof(struct ALVnode));
        newNode->val=val;
        newNode->high=1;
        newNode->left=NULL;
        newNode->right=NULL;
        root=newNode;//root指向新节点
    }
    else{
        //数值大于root的值插入到右边,小于插入到左边,等于插入无效(无法插入)
        if(val<root->val){
            root->left=insert(root->left,val);
            if((root->right==NULL&&root->left->high==2)||(root->right!=NULL&&root->left->high-root->right->high==2)){
                if(val<root->left->val)
                    root=LeftLeft(root);//左单旋
                else
                {
                    root=LeftRight(root);//左右双旋
                }
                
            }
        
        }
        else if(val>root->val){
            root->right=insert(root->right,val);
            if((root->left==NULL&&root->right->high==2)||(root->left!=NULL&&root->left->high-root->right->high==-2)){
                if(val<root->right->val)
                    root=RightLeft(root);//右左双旋
                else
                {
                    root=RightRight(root);//右单旋
                }
                
            }
        }
        //更新树高
        root->high=getHigh(root);
    }
    return root;
}
/*回收树的内存*/
void clear(ALV root){
    if(!root)return;
    clear(root->left);
    clear(root->right);
    free(root);
}
//以“结点值(左子树)(右子树)“的形式打印树,证明它是AVL树
void print(ALV root){
    if(!root)return;
    printf("%d",root->val);
    printf("(");
    print(root->left);
    printf(")");
    printf("(");
    print(root->right);
    printf(")");
}
int main(){
    int num;//要插入的结点数目
    ALV head=NULL;
    scanf("%d",&num);
    int temp;//用来存储要插入的数
    for(int  i=0;i<num;i++){
        scanf("%d",&temp);
        head=insert(head,temp);
    }
    //以“结点值(左子树)(右子树)“的形式打印树,证明它是AVL树
    print(head);
    clear(head);
    return 0;
}

五、测试结果:

输入输出格式说明:

六、算法分析:

该过程从包含其所表示符号的概率的叶节点开始。然后,该过程获取最小两个节点(最小概率),并创建一个将这两个节点作为子节点的新内部节点。新节点的权重设置为子节点权重的总和。然后,我们在新的内部节点和其余节点(即,排除两个叶节点)上再次应用该过程,重复此过程,直到仅剩下一个节点为止,这是霍夫曼树的根。

该构造算法使用优先级队列(最小堆),其中概率最低的节点被赋予最高优先级:

  1. 为每个符号创建一个叶节点,并将其添加到优先级队列。
  2. 当队列中有多个节点时:
    1. 从队列中删除优先级最高(概率最低)的两个节点
    2. 创建一个新的内部节点,并将这两个节点作为子节点,并且其概率等于两个节点的概率之和。
    3. 将新节点添加到队列中。
  3. 其余节点是根节点,树已完成。

由于高效的优先级队列数据结构每次插入需要O(log n)时间,并且具有n个叶子的树具有2 n -1个节点,因此该算法以O(n log n)时间进行操作,算法的空间复杂度为哈弗曼树的内存和存储哈夫曼树节点的最小堆。

标签:HuffmanCode,哈夫曼,int,root,Coding,Huffman,节点,left
来源: https://blog.csdn.net/yoyo_hu/article/details/123229471