Huffman Tree的创建和遍历

  • 实验目的

结点的带权路径长度为从该结点到树根之间的路径长度与结点上权的乘积。树的带权路径长度为树中所有叶子结点的带权路径长度之和,通常记作WPL

若有n个权值为w1,w2,...,wn的结点构成一棵有n个叶子结点的二叉树,则树的带权路径最小的二叉树叫做哈夫曼树或最优二叉树。

 https://images2017.cnblogs.com/blog/1258764/201711/1258764-20171107144200809-496393116.png

在上图中,3棵二叉树都有4个叶子结点abcd,分别带权7524,则它们的带权路径长度为

aWPL = 7*2 + 5*2 + 2*2 + 4*2 = 36

bWPL = 4*2 + 7*3 + 5*3 + 2*1 = 46

cWPL = 7*1 + 5*2 + 2*3 + 4*3 = 35

其中(c)的WPL最小,可以验证,(c)恰为哈夫曼树。

源代码

#include<stdio.h>

#include<stdlib.h>

#define LENGTH 6

 

typedef int ElemType; 

 

typedef struct HuffmanTreeNode{ 

    ElemType data;  //哈夫曼树中节点的权值

    struct HuffmanTreeNode* left; 

    struct HuffmanTreeNode* right; 

}HuffmanTreeNode,*PtrHuffman; 

 

 

/**

 * 创建哈夫曼树

 */

PtrHuffman createHuffmanTree(ElemType arr[]){

    PtrHuffman ptrArr[LENGTH];

    PtrHuffman ptr,pRoot=NULL; 

 

    for (int i = 0; i < LENGTH; i++){  //初始化结构体指针数组,数组中每一个元素为一个结构体指针类型

        ptr = (PtrHuffman)malloc(sizeof(HuffmanTreeNode)); 

        ptr->data = arr[i]; 

        ptr->left = ptr->right = NULL;

        ptrArr[i] = ptr;

    }

   

    for(int i = 1; i < LENGTH; i++){  //进行 n-1 次循环建立哈夫曼树 

        //k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标

        int k1 = -1, k2;

        for(int j = 0; j < LENGTH; j++){ 

            if (ptrArr[j] != NULL && k1 == -1){ 

                k1 = j; 

                continue; 

            } 

            if (ptrArr[j] != NULL){ 

                k2 = j; 

                break; 

            } 

        } 

        //将指针数组中的指针指向的最小值赋值给索引号为k1的,次小值赋值给索引号为k2的

        for (int j = k2; j < LENGTH; j++){

            if(ptrArr[j] != NULL){

                if(ptrArr[j]->data < ptrArr[k1]->data){

                    k2 = k1;

                    k1 = j;

                }else if(ptrArr[j]->data < ptrArr[k2]->data){

                    k2 = j;

                }

            }

        }

        //由最小权值树和次最小权值树建立一棵新树,pRoot指向树根结点

        pRoot = (PtrHuffman)malloc(sizeof(HuffmanTreeNode));

        pRoot->data = ptrArr[k1]->data + ptrArr[k2]->data;

        pRoot->left = ptrArr[k1];

        pRoot->right = ptrArr[k2];

 

        ptrArr[k1] = pRoot; //将指向新树的指针赋给ptrArr指针数组中k1位置

        ptrArr[k2] = NULL; //k2位置为空

    }

 

    return pRoot;

}

 

/**

 * 计算哈夫曼树带权路径长度WPL

 */

ElemType calculateWeightLength(PtrHuffman &ptrTree,int len){

    if(ptrTree==NULL){ //空树返回0

        return 0;

    }else{

        if(ptrTree->left==NULL && ptrTree->right==NULL){ //访问到叶子节点

            return ptrTree->data * len;

        }else{

            return calculateWeightLength(ptrTree->left,len+1) + calculateWeightLength(ptrTree->right,len+1); //向下递归计算

        }

    }

}

 

 

/**

 * 哈夫曼树编码(叶子节点按中序方式依次打印其编码)

 */

void HuffmanCoding(PtrHuffman &ptrTree,int len){

    //静态局部变量相当于全局变量(只是只有在这个函数中能访问,但是生命周期是和全局变量差不多的)函数退出之后变量还在,而且只在第一次进入的时候做初始化,以后会跳过初始化语句,保留原来的值

    static int arr[20]; 

    if(ptrTree != NULL){

        if(ptrTree->left==NULL && ptrTree->right==NULL){

           printf("结点权值为%d的编码: ", ptrTree->data);

           for(int i = 0; i < len; i++){

               printf("%d", arr[i]);     

           }  

           printf(" ");

        }else{

            arr[len] = 0;

            HuffmanCoding(ptrTree->left,len+1);

            arr[len] = 1;

            HuffmanCoding(ptrTree->right,len+1);

        }

    }

}

//打印哈夫曼树中各个节点的孩子节点

//若为叶子节点,则只显示提示信息

//@param node 需要显示孩子节点的父节点

 

void printHuffmanTreeChildNode(PtrHuffman node){

    if(node->left == NULL && node->right == NULL){

        printf("x=%d是哈夫曼树中的叶子节点",node->data);

        printf(" ");

        return;

    }

    if(node->left != NULL){

        printf("x=%d在哈夫曼树中的左孩子节点是lchild=%d",node->data,node->left->data);

        printf(" ");

    }

    if(node->right != NULL){

        printf("x=%d在哈夫曼树中的右孩子节点是rchild=%d",node->data,node->right->data);

        printf(" ");

    }

    printf(" ");

}

 

// 中序打印哈夫曼树的节点

void midOrderprintHuffmanTreeNode(PtrHuffman &pRoot){

    if(pRoot==NULL){

        return;

    }else{

        midOrderprintHuffmanTreeNode(pRoot->left);

        printf("%d ",pRoot->data);

        midOrderprintHuffmanTreeNode(pRoot->right);

    }

}

 

//先序打印哈夫曼树的节点

 

void PreOrderprintHuffmanTreeNode(PtrHuffman &pRoot){

    if(pRoot==NULL){

        return;

    }else{

        printHuffmanTreeChildNode(pRoot); //依次打印哈夫曼树中各个节点的孩子节点

        PreOrderprintHuffmanTreeNode(pRoot->left);

        PreOrderprintHuffmanTreeNode(pRoot->right);

    }

}

 //测试程序入口

int main(){

    ElemType arr[] = {3,9,5,12,6,15};

    PtrHuffman pRoot = createHuffmanTree(arr);  //返回指向哈夫曼树根节点的指针

 

    printf("==========中序打印哈夫曼树节点数据========== ");

    midOrderprintHuffmanTreeNode(pRoot);

    printf(" ");

 

    printf("==========先序打印哈夫曼树节点关系========== ");

    PreOrderprintHuffmanTreeNode(pRoot);

 

    printf("==========计算带权路径长度========== ");

    printf("WeightLength=%d ",calculateWeightLength(pRoot,0));

    printf(" ");

 

    printf("==========各节点的哈夫曼树编码========== ");

    HuffmanCoding(pRoot,0);

 

    fprintf(stdout," ");

   

    return 0;

}

原文地址:https://www.cnblogs.com/zhahu/p/11931428.html