跑骚时刻 - C笔记:赫夫曼编码

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>
#pragma warning(disable:4996)

typedef struct HuffmanTree
{
    int weight;//权值
    int parent;//父节点
    int left;//左子树
    int right;//右子树
};

typedef char *HuffmanCode;//Huffmancode编码

//从1-x个节点选择parent节点为0,权重最小的两个节点
void SelectNode(HuffmanTree *ht, int n, int *bt1, int *bt2){
    int i;
    HuffmanTree *ht1, *ht2, *t;
    ht1 = ht2 = NULL;//初始化两个节点为空
    for (i = 1; i <= n; ++i)//循环处理1-n个节点(包括叶节点和非叶节点)
    {
        if (!ht[i].parent)//父节点为空(节点的parent为0)
        {
            if (ht1 == NULL)//节点指针1为空
            {
                ht1 = ht + i;//指向第i个节点
                continue;//继续循环
            }
            if (ht2 == NULL)
            {
                ht2 = ht + i;
                if (ht1->weight > ht2->weight)//比较两个值的权重,使ht1指向权值大的
                {
                    t = ht2;
                    ht2 = ht1;
                    ht1 = t;
                }
                continue;//继续循环
            }
            if (ht1 && ht2)//若两个值都有效
            {
                if (ht[i].weight <= ht1->weight)//第i个节点权重小于ht1指向的节点
                {
                    ht2 = ht1;//ht2保存ht1,因为这时ht1指向的节点成为第2小的
                    ht1 = ht + i;
                }
                else if (ht[i].weight < ht2->weight)//若第i个节点权重小于ht2指向的权重
                {
                    ht2 = ht + i;
                }
            }
        }
    }
    if (ht1 > ht2)//增加比较,使二叉树左侧为叶节点
    {
        *bt2 = ht1 - ht;
        *bt1 = ht2 - ht;
    }
    else
    {
        *bt1 = ht1 - ht;
        *bt2 = ht2 - ht;
    }
}

void CreateTree(HuffmanTree *ht, int n, int *w){
    int i, m = 2 * n - 1;//总的节点总数
    int bt1, bt2;//二叉树节点序与
    if (n <= 1)//只有一个节点就无法创建
    {
        return;
    }
    for (i = 0; i <= n; ++i)//初始化节点
    {
        ht[i].weight = w[i - 1];
        ht[i].parent = 0;
        ht[i].left = 0;
        ht[i].right = 0;
    }
    for (; i <= m; ++i)//初始化后序节点
    {
        ht[i].weight = 0;
        ht[i].parent = 0;
        ht[i].left = 0;
        ht[i].right = 0;
    }
    for (i = n + 1; i <= m; ++i)//逐个计算非叶节点,创建Huffman树
    {
        SelectNode(ht, i - 1, &bt1, &bt2);
        ht[bt1].parent = i;
        ht[bt2].parent = i;
        ht[i].left = bt1;
        ht[i].right = bt2;
        ht[i].weight = ht[bt1].weight + ht[bt2].weight;
    }
}

//
void HuffmanCoding(HuffmanTree *ht, int n, HuffmanCode *hc){
    char *cd;
    int start, i;
    int current, parent;
    cd = (char*)malloc(sizeof(char)*n);//用来临时存放一个字符编码的结果
    cd[n - 1] = ‘\0‘;//设置字符串结束标志
    for (i = 1; i <= n; i++)
    {
        start = n - 1;
        current = i;
        parent = ht[current].parent;//获取当前节点的父节点;
        while (parent)
        {
            if (current == ht[parent].left)//若该节点的父节点是做左子树
            {
                cd[--start] = ‘0‘;//设置编码为0
            }
            else//若该节点是父节点的右子树
            {
                cd[--start] = ‘1‘;//设置编码为1
            }
            current = parent;//设置当前节点指向父节点
            parent = ht[parent].parent;//获取当前节点的父节点序号;
        }
        hc[i - 1] = (char*)malloc(sizeof(char)*(n - start));
        strcpy(hc[i - 1], &cd[start]);//复制生成生成的编码
    }
    free(cd);//释放编码占用的字符
}

void Encode(HuffmanCode *hc, char *alphabet, char *str, char *code){
    //将一个字符串转换为Huffman编码
    //hc为Huffman编码表,alphabet为对应的字母表,str为需要转换的字符串,code返回转换的结果
    int len = 0, i = 0, j;
    code[0] = ‘\0‘;
    while (str[i])
    {
        j = 0;
        while (alphabet[j] != str[i])
        {
            j++;
        }
        strcpy(code + len, hc[j]);//将对应字母的Huffman编码复制code指定位置
        len = len + strlen(hc[j]);//累加字符串长度
        i++;
    }
    code[len] = ‘\0‘;
}

void Decode(HuffmanTree *ht, int m, char *code, char *alphabet, char *decode){
    //将一个huffman编码组成的字符串转换为明文字符串
    //ht为huffman二叉树,m为字符数量,alphabet为对应的字母表,str需转换的字符串
    int position = 0, i, j = 0;
    m = 2 * m - 1;
    while (code[position])//字符串未结束
    {
        for (i = m; ht[i].left && ht[i].right; position++)
        {
            if (code[position] == ‘0‘)//编码位为0
            {
                i = ht[i].left;
            }
            else
            {
                i = ht[i].right;//处理右子树
            }
        }
        decode[j] = alphabet[i - 1];//得到一个字母
        j++;//处理下一个字符
    }
    decode[j] = ‘\0‘;//字符串结尾
}

//主函数
int main(void){
    int i, n = 4, m;
    char test[] = "DBDACDADCCDBDCBADBCABABA";
    char code[100], code1[100];
    char alphabet[] = { ‘A‘, ‘B‘, ‘C‘, ‘D‘, };//四个字符
    int w[] = { ‘5‘, ‘7‘, ‘2‘, ‘13‘ };//四个字符的权重
    HuffmanTree *ht;
    HuffmanCode *hc;
    m = 2 * n - 1;
    ht = (HuffmanTree *)malloc((m + 1)*sizeof(HuffmanTree));//申请内存,保存赫夫曼树
    if (!ht)
    {
        printf("内存分配失败!");
        exit(0);
    }
    hc = (HuffmanCode *)malloc(n*sizeof(char*));
    if (!hc)
    {
        printf("分配内存失败!");
        exit(0);
    }
    //创建赫夫曼树
    CreateTree(ht, n, w);
    HuffmanCoding(ht, n, hc);//根据赫夫曼树生成的赫夫曼编码
    for (i = 1; i <= n; i++)
    {
        printf("字母:%c,权重:%d,编码为:%s\n", alphabet[i - 1], ht[i].weight, hc[i - 1]);
    }
    Encode(hc, alphabet, test, code);//根据赫夫曼编码生成编码字符串
    printf("\n字符串:\n%s\n转换后为:\n%s\n", test, code);

    Decode(ht, n, code, alphabet, code1);//根据编码字符串生成解码后的字符串
    printf("\n编码:\n%s\n转换后为:\n%s\n", code, code1);
    _getch();
    return 0;
}

跑骚时刻 - C笔记:赫夫曼编码

时间: 2024-08-12 09:08:34

跑骚时刻 - C笔记:赫夫曼编码的相关文章

晨跑调查程序学习笔记

1.网站开发思路,从jsp页面开始写起,然后写servlet,之后写类来保存获取的表单变量,最后将获取的变量写入到数据库. 2.开始写代码前先检查字符集,检查方法: 选中所建立的工程——>Alt+Enter——>修改字符集要尽量和数据库默认字符集一致. 注意:我的MySQL安装时默认字符集不是utf-8,为了和老师讲的一致,我将其修改成了UTF-8. MySQL修改默认字符集的方法见:http://www.cnblogs.com/xingyunblog/p/3836299.html 3.编写表

手把手教你跑Larave框架实战笔记系列之二

系列之一为童鞋们搭好了舞台,今天正式登台跑框架了-- 从"路由"开始,玩一把"Hellow world!" Route::get('/', function () { //return view('welcome'); return 'Hellow world!'; }); laravel 的每一个路由是需要手动定义的,默认欢迎页面(参数) 是 resources/views 目录下的文件名welcome.blade.php去掉 blade.php后缀,对应的路由

iOS 10 消息推送(UserNotifications)秘籍总结(一)

背景 iOS10 新特性一出,各个大神就早已研究新特性能给场景智能化所带来的好处(唉,可惜我只是一个小白).我也被安排适配iOS10的推送工作! Apple 表示这是 iOS 有史以来最大的升级(our biggest release yet),更加智能开放的 Siri .强化应用对 3D Touch 支持. HomeKit .电话拦截及全新设计的通知等等… iOS 10 中将之前繁杂的推送通知统一成UserNotifications.framework 来集中管理和使用通知功能,还增加一些实用

(15)疯狂的程序员----《绝影》

<疯狂的程序员> <疯狂的程序员>1 天已经七分黑了,屋里却还没开灯.这个全身黑衣服的男子突然像想起什么,从包里掏出烟,抽出一只,递给旁边的人:"兄弟,抽烟么?"――那烟是红塔山. 旁边那人连忙一边摆手,一边说:"不,不."语气有点紧张,好像那黑衣服递过来的不是烟,是海洛因. 这个黑衣服的男子,后来的网名叫"绝影".他旁边那个,后来被他们称为"土匪".这件屋子,就是他们大学寝室. 第一天到学校,其实没有

谷歌敦促android开发者停止使用菜单按钮

鲜血溅落在那泛光的绿珠之上,顿时惊变忽生,那珠子猛然绽放出耀眼光芒,一股劲风在周围肆虐起来,吹动着那些碎石飞舞起来,但是凌雪和珠子却被笼罩在一个帷幕之中,丝毫不受到影响. 飞火眼里立刻透出向往的神色,笑道"这样的话,那就太好了!我只是怕雪月的门槛高,不肯要我们飞火这种小行会--" 魂殇挥舞长剑,猛然一个剑荡四方,这可比灵儿的剑荡四方要原汁原味多了,一击之下,强悍如天将军亦被震得连连飞退,好不容易稳住了身影,魂殇却又已经掩杀而至,利剑一挥,重击之后,一个紫色的骷髅光芒出现在天将军的头顶之

《Python基础教程(第二版)》学习笔记 -&gt; 第十章 充电时刻 之 标准库

SYS sys这个模块让你能够访问与Python解释器联系紧密的变量和函数,下面是一些sys模块中重要的函数和变量: 函数和变量 描述 argv 命令行参数,包括脚本和名称 exit([arg])                退出当前的程序,可选参数为给定的返回值或者错误信息 modules 映射模块名字到载入模块的字典 path 查找模块所在目录的目录名列表 platform 平台标识符 stdin 标准输入流-- 一个类文件对象 stdout 标准输出流-- 一个类文件对象 stderr

MordenPHP阅读笔记(一)——先跑再说,跑累了再走

---恢复内容开始--- 后台一大堆半成品,或者是几乎不成的... 这本书不错,起码是别人推荐的,然后也是比较新的东西,学哪本不是学嘛,关键是得看. 今儿个网不好,科研所需的代码下不到,看书做笔记吧. 这本书基本将的是5.4版本后的一些新变化,写的浅显易懂,虽然鄙人走的还不顺溜,跑一跑也摔不到哪儿去,跑累了我有的是走的机会~~ (一)特性 一.命名空间 一个文件一个类,用了命名空间方便互相调用: 1 // 2 //Namespace 3 // 4 namespace ModernPHP\feat

python基础教程_学习笔记12:充电时刻——模块

充电时刻--模块 python的标准安装包括一组模块,称为标准库. 模块 >>> import math >>> math.sin(0) 0.0 模块是程序 任何python程序都可以作为模块导入. $ cat hello.py #!/usr/bin/python print "Hello,signjing!" $ ./hello.py Hello,signjing! 假设将python程序保存在/home/ggz2/magiccube/mysh/p

嵌入式学习笔记202-Linux kernel跑起来

在<嵌入式学习笔记104-uboot_1.1.6移植(4)>.<嵌入式学习笔记300-linux根文件系统搭建>uboot和根文件系统都准备好的前提下,此时的kernel已基本可以跑起来,不过还差正确的挂起文件系统,其中uboot传给kernel的挂载处是root=/dev/mtdblock2 ,而在<嵌入式学习笔记201-Linux kernel动起来>的打印分区信息是: mtd: partition "Boot Agent" doesn't en