最优二叉树(简易版本)

最优二叉树——哈夫曼树

 

一:什么是最优二叉树:

  就是从已给出的目标带权结点(单独的结点) 经过一种方式的组合形成一棵树.使树的权值最小。

    注意: 带权值的结点都是叶子结点。权值越小的结点,其到根结点的路径越长

 

二:哈夫曼算法:

 (1)根据给定的n个权值wl,w2,…,wn构成n棵二叉树的森林F={T1,T2,…,Tn},其中每棵二叉树Ti中都只有一个权值为wi的根结点,其左右子树均空。

 (2)在森林F中选出两棵根结点权值最小的树(当这样的树不止两棵树时,可以从中任选两棵),将这两棵树合并成一棵新树,为了保证新树仍是二叉树,需 要增加一个新结点作为新树的根,并将所选的两棵树的根分别作为新根的左右孩子(谁左,谁右无关紧要),将这两个孩子的权值之和作为新树根的权值。   

 (3)对新的森林F重复(2),直到森林F中只剩下一棵树为止。这棵树便是哈夫曼树。

  注意: ① 初始森林中的n棵二叉树,每棵树有一个孤立的结点,它们既是根,又是叶子 ② n个叶子的哈夫曼树要经过n-1次合并,产生n-1个新结点。最终求得的哈夫曼树中共有2n-1个结点。 ③ 哈夫曼树是严格的二叉树,没有度数为1的分支结点。

 

三:最优二叉树实现思路:

  设置一个结构数组HuffNode保存哈夫曼树中各结点的信息 , 具有n个叶子结点的哈夫曼树共有2n-1个结点,所以数组HuffNode的大小设置为2n-1 先将由n个字符形成的n个叶结点存放到数组HuffNode的前n个分量中,然后 不断将两个小子树合并为一个较大的子树,每次构成的新子树的根结点顺序放到HuffNode数组中的前n个分量的后面。

 

四:参考代码:(之后记得看一下)

#include <stdio.h>
#include <stdlib.h>
#define m 100
​
​
struct ptree              //定义二叉树结点类型
{
    int w;                   //定义结点权值
    struct ptree* lchild;     //定义左子结点指针
    struct ptree* rchild;     //定义右子结点指针
};
​
​
​
struct pforest              //定义链表结点类型
{
    struct pforest* link;
    struct ptree* root;
};
​
​
​
int WPL = 0;                  //初始化WTL为0
struct ptree* hafm();
void travel();
struct pforest* inforest(struct pforest* f, struct ptree* t);
​
void travel(struct ptree* head, int n)
{
    //为验证harfm算法的正确性进行的遍历
    struct ptree* p;
    p = head;
    if (p != NULL)
    {
        if ((p->lchild) == NULL && (p->rchild) == NULL) //如果是叶子结点
        {
            printf("%d ", p->w);
            printf("the hops of the node is: %d/n", n);
            WPL = WPL + n * (p->w);    //计算权值
        }//if
        travel(p->lchild, n + 1);
        travel(p->rchild, n + 1);
    }//if
}//travel
​
​
​
struct ptree* hafm(int n, int w[m])
{
    struct pforest* p1, * p2, * f;
    struct ptree* ti, * t, * t1, * t2;
    int i;
    f = (pforest*)malloc(sizeof(pforest));
    f->link = NULL;
    
    for (i = 1; i <= n; i++)          //产生n棵只有根结点的二叉树
    {
        ti = (ptree*)malloc(sizeof(ptree));//开辟新的结点空间
        ti->w = w[i];               //给结点赋权值
        ti->lchild = NULL;
        ti->rchild = NULL;
        f = inforest(f, ti);
        //按权值从小到大的顺序将结点从上到下地挂在一颗树上     
    }//for
while (((f->link)->link) != NULL)//至少有二棵二叉树
    {
        p1 = f->link;
        p2 = p1->link;
        f->link = p2->link;           //取出前两棵树
        t1 = p1->root;
        t2 = p2->root;
        free(p1);                 //释放p1
        free(p2);                 //释放p2
​
        t = (ptree*)malloc(sizeof(ptree));//开辟新的结点空间
        t->w = (t1->w) + (t2->w);        //权相加
        t->lchild = t1;
        t->rchild = t2;             //产生新二叉树
        f = inforest(f, t);
    }//while
​
    p1 = f->link;
    t = p1->root;
    free(f);
    return(t);                  //返回t
}
​
​
​
pforest* inforest(struct pforest* f, structptree* t)
{
    //按权值从小到大的顺序将结点从上到下地挂在一颗树上     
    struct pforest* p, * q, * r;
    struct ptree* ti;
    r = (pforest*)malloc(sizeof(pforest)); //开辟新的结点空间
    r->root = t;
    q = f;
    p = f->link;
    
    while (p != NULL)            //寻找插入位置
    {
        ti = p->root;
        if (t->w > ti->w)         //如果t的权值大于ti的权值
        {
            q = p;
            p = p->link;             //p向后寻找
        }//if
        else
            p = NULL;                  //强迫退出循环
    }//while
​
    r->link = q->link;
    q->link = r;                 //r接在q的后面
    return(f);                 //返回f
}
​
​
​
void InPut(int& n, int w[m])
{
    printf("please input the sum ofnode/n"); //提示输入结点数
    scanf("%d", &n);      //输入结点数
    printf("please input weight of everynode/n"); //提示输入每个结点的权值
    for (int i = 1; i <= n; i++)
        scanf("%d", &w[i]);  //输入每个结点权值
}
​
​
int main()
{
    struct ptree* head;
    int n, w[m];
    InPut(n, w);
    head = hafm(n, w);
    travel(head, 0);
    printf("The length of the best path isWPL=%d", WPL);//输出最佳路径权值之和
    return 1;
}

 

原文地址:https://www.cnblogs.com/instead-everyone/p/13733937.html