数据结构与算法 -- 哈夫曼树思想与创建详解1

PS:什么是哈夫曼树?

  给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

计算规则:

  假设一组权值,一个权值是一个结点,12  34  2  5  7  ,在这其中找出两个最小的权值,然后组成一个新的权值,再次找出最小的权值结点。如图:

问题:

  1:如果程序找出两个最小的权值,把两个权值最小的相加结果再次添加到数组中呢。

  2:完成第一步后,又怎么让他们成为一个二叉树呢。

  3:这个二叉树的结构体怎么定义呢,这组带有权值的结点又以什么方式存在呢。

思路:

  对于这种陆续找出两个最小权值的算法可以利用排序的方式,从小到大排序,那么最左边的就是最小的,这样一来最小的权值可以挑选出来了,接下来再利用特定的结构体(都有左孩子和右孩子还有存放权值的data域)让每一个权值结点存在一个数组中。这样子不断的操作数组,从数组中的5个元素到只有1个元素为止,此时的这一个元素就是二叉树的跟。然后再利用遍历方式打印这个二叉树即可。

代码实现:

结构体定义

一个二叉树的结构体,一个数组的结构体。可以看出数组的结构体内部是包含一个二叉树结点的结构体的。

/**
 * Created by 刘志通 on 2018/11/22.
 * @describe 哈夫曼树的简介
 * 编程思想:
 * 1:方式简介:
 *      利用数组(二叉树结构体类型),来存放初始权值(首次认为权值就是一个树跟,左右孩子分别是NULL),在数组初始化的之后排序,然后拿出index=0,1,更新
 *      权值根,
 * 2:所用知识:
 *      数组,链表存储,二叉树结构体。
 */
#include "stdlib.h"
#include "stdio.h"

/**
 * @describe 二叉树结构体
 * 结构形式:Lchild data Rchild
 * */
typedef struct twoTree {
    int data;
    struct twoTree *Lchild, *Rchild;
} twoTree, *twoTreeL;
typedef struct {
    twoTreeL data[100];
    int length;
} arrayMy;

初始化数组

arrayMy InitList(arrayMy &L) {
    // 初始化表,一个空表。
    L.length = 0;
    return L;
}
/**
 * 冒泡排序
 * */
 void swap(int *weigth,int n){
    for (int i = 0; i < n; i++){
        for (int j = 0; j+1<n-i; j++){
            if (weigth[j] > weigth[j+1]){
                int temp;
                temp = weigth[j];
                weigth[j] = weigth[j+1];
                weigth[j+1] = temp;
            }
        }
    }
 }

数组的特定操作

对于初始化数组可以看出是把每一个元素写成了二叉树结构的一个结点添加到了一维数组中,初始状态左右孩子都是NULL,data是权值,还有两个方法是插入和删除,之所以有这两个方法是因为,要对数组进行操作的时候需要对数组元素更新,找出前两个最小元素后,要立马删除这两个元素,然后把这两个元素相加得到的结果添加到数组中,添加时要有序添加,否则还要排序。这里面有注释,不懂也可以留言。

int ListDelete(arrayMy &L, int pos) {
    if (pos > L.length) {
        printf("删除角标不合法");
        return -1;
    }
    printf("\n删除%d\n",L.data[pos]->data);
    int i;
    //在插入的同时,i要保证在pos以及pos后方,入1,2,3,4,5当在第3个插入时,须把原有的第三个数据以及以后数据后移一位,空出第三个位置。
    for (i = pos; i <= L.length; i++) {
        L.data[i - 1] = L.data[i];
    }
    L.length--;
    return 0;//返回0表示成功;
}

int ListInsert(arrayMy &L, twoTreeL data) {
    printf("插入%d\n",data->data);

    int pos = 0;
    for (int i = 0; i < L.length; i++) {
        if (data->data >= L.data[i]->data) {
            pos = i;
            printf("pos---%d\n",pos);
            break;
        }
    }
    int i;
    //在插入的同时,i要保证在pos以及pos后方,入1,2,3,4,5当在第3个插入时,须把原有的第三个数据以及以后数据后移一位,空出第三个位置。
    for (i = L.length; i > pos; i--) {
        L.data[i] = L.data[i - 1];
    }
    L.data[pos] = data;
    L.length++;
    return 0;
}

/**
 * 数组初始化
 * */
void arrayInit(int *weigth, int n, arrayMy &arr) {

    int i;
    for (i = 0; i < n; i++) {
        twoTreeL treeL = (twoTreeL) malloc(sizeof(twoTree));
        treeL->Lchild = NULL;
        treeL->Rchild = NULL;
        treeL->data = weigth[i];
        arr.data[i] = treeL;
        arr.length++;
//        arr->lenght++;
    }
    printf("初始化成功");
}

哈夫曼算法

这是最重要的一点,首先拿到前两个权值结点,相加计算得到结果赋值给新建结点,然后删除数组中的前两个结点,插入新建结点,然后递归重复此操作。

/**
 * @describe 哈夫曼算法
 * */

arrayMy haFuManSF(arrayMy &arr) {
    printf("\n个数%d", arr.length);
    if (arr.length >= 2) {
        twoTreeL tA = arr.data[0];//拿到第一个结点data数,(权值最小)。
        twoTreeL tB = arr.data[1];//拿到第二个结点data数,(权值第二小)。
        twoTreeL tc = (twoTreeL) malloc(sizeof(twoTree));//创建新的结点
        tc->data = tA->data + tB->data;//相加结果加入到新建的data域。
        tc->Lchild = tA;//让A称为左子树
        tc->Rchild = tB;
        ListDelete(arr, 0);//删掉数组的前两个结点元素。
        ListDelete(arr, 0);
//        printf(" geshu %d  ",arr.length);
        printf("还剩  ");
        for(int i=0;i<arr.length;i++){
            printf("%d ", arr.data[i]->data);
        }
        ListInsert(arr, tc);//插入新建结点元素。
        printf("\n插入后  ");
        for(int i=0;i<arr.length;i++){
            printf("%d ", arr.data[i]->data);
        }
        haFuManSF(arr);//然后递归,重复上面操作。
    } else {
        return arr;
    }
//    printf(" geshu %d  ",arr.length);

    return arr;
}

递归遍历

//先中后序遍历二叉树
void diGuiBianLi(twoTreeL tL, int xl) {
    if (tL == NULL) {
        return;
    } else {
        if (xl == 1) {
            //先序遍历
            printf("%d ", tL->data);
            diGuiBianLi(tL->Lchild, xl);
            diGuiBianLi(tL->Rchild, xl);
        } else if (xl == 2) {
            //中序遍历
            diGuiBianLi(tL->Lchild, xl);
            printf("%d ", tL->data);
            diGuiBianLi(tL->Rchild, xl);
        } else if (xl == 3) {
            //后序遍历
            diGuiBianLi(tL->Lchild, xl);
            diGuiBianLi(tL->Rchild, xl);
            printf("%d ", tL->data);
        }
    }
}

调试:main

int main(void) {
    //新建权值数组
    int weigth[5] = {3,1 , 12, 4, 14};
    //排序(升序)
    swap(weigth,5);
    //创建数组(存放权值和个数)
    arrayMy arr;
    arr = InitList(arr);
    arrayInit(weigth, 5, arr);
    //检测数组是否排序好
    for (int i = 0; i < 5; i++) {
        printf("%d ", arr.data[i]->data);
    }
    //哈夫曼编程思想
    arr = haFuManSF(arr);
    twoTreeL treeL=arr.data[0];
    printf("\n");
    //先中后遍历
    printf("\n先序遍历: ");
    diGuiBianLi(treeL,1);
    printf("\n中序遍历:");
    diGuiBianLi(treeL,2);
    printf("\n后序遍历:");
    diGuiBianLi(treeL,3);

    return 0;
}

完。

原文地址:https://www.cnblogs.com/cmusketeer/p/10199706.html

时间: 2024-09-29 22:18:47

数据结构与算法 -- 哈夫曼树思想与创建详解1的相关文章

哈夫曼树(三)之 Java详解

前面分别通过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 哈夫曼树的介绍 Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树. 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树. 这

基础数据结构-二叉树-赫夫曼树的解码(详解)

本篇是上一篇赫夫曼树构建与编码的后续,稍微详细讲一下解码的算法. Huffman解码算法流程: 1.定义指针p指向赫夫曼树结点,实际是记录结点数组的下标: 2.定义指针i指向编码串,定义ch逐个取编码串的字符: 3.初始化:读入编码串,设置p指向根结点,i为0: 4.执行以下循环: a)取编码串的第i个字符放入ch: b)如果ch是字符0,表示往左孩子移动,则p跳转到右孩子: c)如果ch是字符1,表示往右孩子移动,则p跳转到右孩子: d)如果ch非0非1,表示编码串有错误,输出error表示解

5678: 数据结构实验:哈夫曼树和编码

描述 当用 n 个结点(都做叶子结点且都有各自的权值)试图构建一棵树时,如果构建的这棵树的带权路径长度最小,称这棵树为“最优二叉树”,有时也叫“赫夫曼树”或者“哈夫曼树”. 现给定若干权值,请构建一棵哈夫曼树,并输出各个权值对应的哈夫曼编码长度. 哈夫曼树中的结点定义如下: //哈夫曼树结点结构 typedef struct { int weight;//结点权重 int parent, left, right;//父结点.左孩子.右孩子在数组中的位置下标 }HTNode, *HuffmanTr

20172301 哈夫曼树实验报告

20172301 哈夫曼树实验报告 课程:<Java软件结构与数据结构> 班级: 1723 姓名: 郭恺 学号:20172301 实验教师:王志强老师 实验日期:2018年12月9日 必修/选修: 必修 一.实验内容 二.实验过程 哈夫曼树 哈夫曼(Haffman)树,也称最优二叉树,是指对于一组带有确定权值的叶结点.构造的具有最小带权路径长度的二叉树. 二叉树的路径长度是指由根结点到所有的叶结点的路径长度之和. 一棵二叉树要想它的带权路径长度最小,必须使权值越大的叶结点越靠近根结点,而权值越

算法和数据结构(哈夫曼树)

哈夫曼树 百科:http://baike.baidu.com/view/127820.htm?fr=aladdin 两种题型: 1.求编码 2.求带权路径长度,例子:http://zhidao.baidu.com/link?url=YzBU4T8NZxwdNBw7I5Sy7WK-YjGVOLXbV01tYAvdFvsedhnaJHw7nsAxq01r0WGseg-dr1d2GcRMd9RAIr77Ya

数据结构(三):非线性逻辑结构-特殊的二叉树结构:堆、哈夫曼树、二叉搜索树、平衡二叉搜索树、红黑树、线索二叉树

在上一篇数据结构的博文<数据结构(三):非线性逻辑结构-二叉树>中已经对二叉树的概念.遍历等基本的概念和操作进行了介绍.本篇博文主要介绍几个特殊的二叉树,堆.哈夫曼树.二叉搜索树.平衡二叉搜索树.红黑树.线索二叉树,它们在解决实际问题中有着非常重要的应用.本文主要从概念和一些基本操作上进行分类和总结. 一.概念总揽 (1) 堆 堆(heap order)是一种特殊的表,如果将它看做是一颗完全二叉树的层次序列,那么它具有如下的性质:每个节点的值都不大于其孩子的值,或每个节点的值都不小于其孩子的值

一步一步写算法(之哈夫曼树 上)

原文:一步一步写算法(之哈夫曼树 上) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在数据传输的过程当中,我们总是希望用尽可能少的带宽传输更多的数据,哈夫曼就是其中的一种较少带宽传输的方法.哈夫曼的基本思想不复杂,那就是对于出现频率高的数据用短字节表示,对于频率比较低得数据用长字节表示. 比如说,现在有4个数据需要传输,分别为A.B.C.D,所以一般来说,如果此时没有考虑四个数据出现的概率,那么我们完全可以这么分配,平均长度为2, /

数据结构第三部分:树与树的表示、二叉树及其遍历、二叉搜索树、平衡二叉树、堆、哈夫曼树、集合及其运算

参考:浙大数据结构(陈越.何钦铭)课件 1.树与树的表示 什么是树? 客观世界中许多事物存在层次关系 人类社会家谱 社会组织结构 图书信息管理 分层次组织在管理上具有更高的效率! 数据管理的基本操作之一:查找(根据某个给定关键字K,从集合R 中找出关键字与K 相同的记录).一个自然的问题就是,如何实现有效率的查找? 静态查找:集合中记录是固定的,没有插入和删除操作,只有查找 动态查找:集合中记录是动态变化的,除查找,还可能发生插入和删除 静态查找——方法一:顺序查找(时间复杂度O(n)) int

【数据结构】赫夫曼树的实现和模拟压缩(C++)

赫夫曼(Huffman)树,由发明它的人物命名,又称最优树,是一类带权路径最短的二叉树,主要用于数据压缩传输. 赫夫曼树的构造过程相对比较简单,要理解赫夫曼数,要先了解赫夫曼编码. 对一组出现频率不同的字符进行01编码,如果设计等长的编码方法,不会出现混淆的方法,根据规定长度的编码进行翻译,有且只有一个字符与之对应.比如设计两位编码的方法,A,B,C,D字符可以用00-11来表示,接收方只要依次取两位编码进行翻译就可以得出原数据,但如果原数据只由n个A组成的,那发出的编码就是2n个0组成,这样的