在compfilelength++处显示已执行断点指令

那位可以看看这代码出了什么问题,整个人都要抓狂了

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#include <conio.h>
#include <limits.h>
// 构造 Huffman 树的数据
// 包含值域和权重
typedef struct _DATA_
{
    unsigned char value;    // 值域
    int weight;                // 权重    
}Data;
// Huffman 树结点类型
// 值域、权重、父节点、左孩子、右孩子及 Huffman 编码
// 采用一维数组存储 Huffman 树
// parent, lchild, rchild 取值为结点在数组中的下标
// Huffman 编码采用动态数组
typedef struct _HUFFMANNODE_
{
    unsigned char value;        // 值域
    int weight;                    // 权重
    int parent, lchild, rchild;    // parent , leftchild , rightchild
    char* code;                    //  Huffman 编码
}HuffmanNode, * HuffmanTree;
///////////////////////////////////
// 函数声明
void descendsort(HuffmanNode ht[], int n);
void InitHuffmanTree(HuffmanTree ht, Data data[], int dn);
int CreateHuffmanTree(HuffmanTree ht, int dn);
void CreateHuffmanCode(HuffmanTree ht, int leafnum);
int ScanFile(const char* filename, Data data[], int dn);
int CompressFile(const char* filename, const char* compfilename);
int DeCompress(const char* compfilename, const char* filename);
void Code(HuffmanTree ht, int leafnum, char str[]);
void DeCode(HuffmanTree ht, int leafnum, char code[]);
///////////////////////////////////
/*/////////////////////////////////
// 采用一维数组 ht 存储 Huffman 树
// 对数组 ht 中的前 n 个结点,按权重以 降序 排序
//
// ht:        存放 Huffman 树的一维数组
// 也可表示为  HuffmanTree ht
// void descendsort( HuffmanTree ht, int n )
*//////////////////////////////////
void descendsort(HuffmanNode ht[], int n)
{
    int i, j;
    HuffmanNode temp;
    for (i = 0; i < n - 1; i++)
    {
        for (j = i + 1; j < n; )
        {
            if (ht[i].weight > ht[j].weight)
                j++;
            else
            {
                temp = ht[i];
                ht[i] = ht[j];
                ht[j] = temp;
                j++;
            }
        }
    }
}
/*/////////////////////////////////
// 利用数组 data 中的 dn 个数据
// 对存储在一维数组 ht 中的 Huffman 树进行初始化
// 根据 Huffman 树的性质
// 把前 dn 个结点作为叶子结点,用 data 中的数据对成员 value、weight进行赋值
// 成员 parent 赋值为 0,成员 lchild、rchild 赋值为 -1
//
// dn 个叶子结点的 Huffman 树,共有 2 * dn - 1 个结点
// 对于其它结点,成员 weight、parent赋值为 0
// 成员 lchild、rchild 赋值为 -1
*//////////////////////////////////
void InitHuffmanTree(HuffmanTree ht, Data data[], int dn)
{
    int i;
    for (i = 0; i < dn; i++)
    {
        ht[i].value = data[i].value;
        ht[i].weight = data[i].weight;
        ht[i].parent = 0;
        ht[i].lchild = -1;
        ht[i].rchild = -1;
    }
    for (; i < 2 * dn - 1; i++)
    {
        ht[i].weight = 0;
        ht[i].parent = 0;
        ht[i].lchild = -1;
        ht[i].rchild = -1;
    }
}
/*/////////////////////////////////
// 根据 ht 中的叶子结点,构造带权路径长度(WPL)最小的 Huffman 树
// ht :存放 Huffman 树结点的数组,也可表示为 HuffmanNode ht[]
// int CreateHuffmanTree( HuffmanNode ht[], int dn )
// 叶子结点位于 ht[0] - ht[dn-1]
// 完成后,返回叶子结点的个数 leafnum
*//////////////////////////////////
int CreateHuffmanTree(HuffmanTree ht, int dn)
{
    int leafnum = 0, nodenum; // 叶子结点数、全部结点数 nodenum = 2 * leafnum - 1
    // 调用 descendsort 对前面的 dn 个叶子结点进行排序
    descendsort(ht, dn);
    // 按权重从大到小排序后
    // 从前面的 dn 个叶子结点中,排除权重为0的结点
    // 用 leafnum 记录新的叶子结点数   leafnum <= dn
    for (int i = 0; i < dn; i++)
    {
        if (ht[i].weight == 0)
        {
            break;
        }
        leafnum++;
    }
    nodenum = 2 * leafnum - 1;
    // 根据 ht 中的叶子结点,构造带权路径长度(WPL)最小的 Huffman 树
    // 每次选择两个权重最小、且 parent 为 0 的结点
    // INT_MAX 表示 int 类型的最大值
    int i, min1, min2, x, y;
    // 合并叶子结点 
    for (i = leafnum; i < nodenum; i++)
    {
        min1 = min2 = INT_MAX;
        x = y = 0;
        // 在所有 weight 不为 0 的节点中选出 2 个深度最小的
        for (int j = 0; j < i; j++)
        {
            if (ht[j].weight == 0)
            {
                continue;
            }
            if (ht[j].parent == 0)
            {
                if (ht[j].weight < min1)
                {
                    min2 = min1;
                    y = x;
                    min1 = ht[j].weight;
                    x = j;
                }
                else if (ht[j].weight < min2)
                {
                    min2 = ht[j].weight;
                    y = j;
                }
            }
        }
        // 合并节点
        ht[x].parent = i;
        ht[y].parent = i;
        ht[i].lchild = x;
        ht[i].rchild = y;
        ht[i].weight = min1 + min2;
    }
    /////////////////////////////////
    // 以 leafnum 作为返回值
    return leafnum;
}
/*/////////////////////////////////
// 对 Huffman 树中的叶子结点进行编码
// ht:        存放 Huffman 树结点的数组
// leafnum:叶子结点的个数,叶子结点位于 ht[0] - ht[leafnum-1]
// 从每个叶子结点出发,利用 parent 向上直到根结点
// 根结点的标志是其 parent 为 0
// 根据路径进行编码,左 0 右 1
// 编码长度不超过 leafnum
// 需要用 calloc 生成动态数组来保存编码
*//////////////////////////////////
void CreateHuffmanCode(HuffmanTree ht, int leafnum)
{
    int index, len = leafnum;
    char* code;
    code = (char*)calloc(len, sizeof(char)); //  暂时存放一个 Huffman 编码,编码长度不超过 leafnum
    for (int i = 0; i < leafnum; ++i) // 逐个处理叶子结点
    {
        index = len - 1;
        // 由于是从叶子开始,编码按从后向前的顺序生成
        // 例如:编码 "10100" 是按照 0 0 1 0 1 的顺序生成的
        // code: [leafnum-1]   [leafnum-2]     [ ]   [ ]   [ ]   [leafnum-6 ]   ……  [2] [1] [0]      
        //         空字符          '0'         '0'   '1'   '0'        '1'         
        //                                                      index 为此处的下标值 leafnum-6
        // 最后根据码长创建动态数组  ht[i].code 
        // 用 strcpy 将 code 中的编码复制到 ht[i].code 中
        // 此处添加代码
        ht[i].code = (char*)calloc(len, sizeof(char));
        int cur = i, p = ht[cur].parent;
        while (p != 0)
        {
            if (ht[p].lchild == cur)
                code[--index] = '0';
            else
                code[--index] = '1';
            cur = p;
            p = ht[p].parent;
        }
        strcpy_s(ht[i].code, len - index, &code[index]);
    }
    free(code);
}
/*/////////////////////////////////
// 根据 Huffman 树,对 str 中的字符串进行编码并显示
// ht :存放 Huffman 树结点的数组,也可表示为 HuffmanNode ht[]
// leafnum:叶子结点的个数,叶子结点位于 ht[0] - ht[leafnum-1]
// 相应的 Huffman 码在 ht[i].code 中
// str;存放字符串的数组
*//////////////////////////////////
void Code(HuffmanTree ht, int leafnum, char str[])
{
    for (int i = 0; 'a' <= str[i] && str[i] <= 'z'; i++)
    {
        for (int j = 0; j < leafnum; j++)
        {
            if (ht[j].value == str[i])
            {
                printf("%s", ht[j].code);
                break;
            }
        }
    }
    printf("\n");
}
/*/////////////////////////////////
// 根据 Huffman 树进行译码,得到原先的字符串
// ht :存放 Huffman 树结点的数组,也可表示为 HuffmanNode ht[]
// leafnum:叶子结点的个数,叶子结点位于 ht[0] - ht[leafnum-1]
// code;待译码的二进制串
*//////////////////////////////////
void DeCode(HuffmanTree ht, int leafnum, char code[])
{
    int nodenum = 2 * leafnum - 1;    // 总的结点数   
    int root = nodenum - 1;            // Huffman 树根结点的下标
    int cur = root;
    // 从根结点开始,根据 code 中的编码左下或右下,直到叶子结点
    // 用 cur 记录当前结点,到达叶结点的条件是 cur < leafnum
    for (int i = 0; i < strlen(code); i++)
    {
        if (code[i] == '0')
            cur = ht[cur].lchild;
        else
            cur = ht[cur].rchild;

        if (cur < leafnum)
        {
            printf("%c", ht[cur].value);
            if (code[i] == '0' || code[i] == '1')
            {
                cur = root;
                continue;
            }
            else
                break;
        }
    }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*/////////////////////////////////
// 扫描文件,统计权重
//
// 利用 Huffman 树进行文件压缩时
// 将构成文件的二进制串
// 按照 8 位一组,分解为字节序列
//
// 以字节为单位读取文件 filename
// 将每个字节值出现的次数作为权重
// 记录在数组 data 中
// 由于一个字节的取值范围为 0 ~ 255 共 256 个值
// data[0].value = 0        data[0].weight 为 0000 0000 在文件中出现的次数
// data[1].value = 1        data[1].weight 为 0000 0001 在文件中出现的次数
// data[255].value = 255    data[255].weight 为 1111 1111 在文件中出现的次数
// 参数 dn 为数组 data 的长度
// 在调用函数时被传值为 256
//
*//////////////////////////////////
int ScanFile(const char* filename, Data data[], int dn)
{
    FILE* fp;
    fp = fopen(filename, "rb");
    if (fp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    // 对 data[i].value 和 data[i].weight 赋初始值
    for (int i = 0; i < 256; i++)
    {
        data[i].value = i;
        data[i].weight = 0;
    }
    ///////////////////////////////////
    // 用 fread 从文件中每次读取一个字节
    // 统计每个字节所取值的出现频率
    // 同时记录文件的长度
    int filelength = 0;    // 文件长度,以字节为单位
    unsigned char ch;
    int m=fread(&ch, sizeof(unsigned char), 1, fp);
    while(m==1)
    {
        data[ch].weight++;
        filelength++;
        m=fread(&ch, sizeof(unsigned char), 1, fp);
    }
    fclose(fp);
    // 返回文件的长度,以字节为单位
    return filelength;
}

/*/////////////////////////////////
// 利用 Huffman 树进行文件压缩时
// 将构成文件的二进制串
// 按照 8 位一组,分解为字节序列
// 由于一个字节的取值范围为 0 ~ 255 共 256 个值
// 将每个字节值出现的次数作为权重
// 记录在长度为 256 的数组 data 中
// filename:        原文件名
// compfilename:    压缩文件名
// 压缩成功,返回  1      压缩失败,返回 0
*//////////////////////////////////
int CompressFile(const char* filename, const char* compfilename)
{
    int valuenum = 256;
    int filelength;        // 以字节为单位的文件长度
    int leafnum;        // 叶子结点数
    Data* data;            // 存放数据的值和权重
    HuffmanTree ht;
    ///////////////////////////////////
    // 不同于英文字母的权重,压缩文件时,需要通过扫描文件进行统计,得到权重数据 
    data = (Data*)calloc(valuenum, sizeof(Data));        // valuenum 为 256
    // 扫描待压缩的文件,统计每个字节值的频率
    // 同时统计文件长度,以字节为单位
    filelength = ScanFile(filename, data, valuenum);
    if (0 == filelength) return 0;        // 扫描失败
    ht = (HuffmanTree)calloc(2 * valuenum - 1, sizeof(HuffmanNode));    // 结点总数不会超过 2*valuenum-1
    // 初始化顺序存储的 Huffman 树结点
    InitHuffmanTree(ht, data, valuenum);
    // 创建 Huffman 树
    leafnum = CreateHuffmanTree(ht, valuenum); // leafnum <= valuenum 有些值可能在文件中未出现
    //生成 Huffman 编码
    CreateHuffmanCode(ht, leafnum);
    // 测试代码,显示 Huffman 编码,完成后删除
    for (int i = 0; i < leafnum; i++)
    {
        printf("[ %3d ] : %s\n", ht[i].value, ht[i].code);
        if ((i + 1) % 32 == 0) _getch();
    }//////////////////////////////////
    ///////////////////////////////////
    // 生成压缩文件
    int i;
    FILE* fp, * cfp;
    int compfilelength = 0;  // 压缩后文件的长度
    // 首先将叶子结点数、每个叶子结点的值和权重、文件原始长度依次写入压缩文件
    ///////////////////////////////////
    cfp = fopen(compfilename, "wb");    // 新建二进制文件并写入
    if (cfp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    fwrite(&leafnum, sizeof(int), 1, cfp);    // 写入叶子结点数
    // 此处补充代码,写入其它数据
    for (i = 0; i < leafnum; i++)
    {
        fwrite(&ht[i].value, sizeof(char), 1, cfp);
        fwrite(&ht[i].weight, sizeof(int), 1, cfp);
    }
    fwrite(&filelength, sizeof(int), 1, cfp);
    // 开始对文件进行压缩,需要再次扫描原始文件
    fp = fopen(filename, "rb");
    if (fp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    ///////////////////////////////////
    unsigned char ch;    // 每次写入一个字节
    char* code;
    code = (char*)calloc(2 * valuenum, sizeof(char));    // 存放 Huffman 编码
    unsigned char buf = 0;    // 缓存当前正在拼接的字节
    int remain_bit = 8;    // 跟踪当前字节缓存中剩余的未拼接位数
    fread(&ch, sizeof(unsigned char), 1, fp);        // 从原文件中读取第一个字节
    while (!feof(fp))
    {
        // 根据 ch 的值找到对应的叶子结点
        // 用叶子结点的 Huffman 编码,取代 ch 写入压缩文件中
        // 难点在于,写入必须以字节为单位,而每个编码的长度并不是字节的整数倍
        // 例如某个编码为 9 位,需要先把前 8 位作为一个字节写入
        // 剩下的 1 位和后面的编码凑够一个字节后再次写入
        // 还要注意,编码是字符串,"11110000"是8个字节
        // 要变为二进制数 11110000 后才是一个字节
        // 考虑学习使用位运算运算符 <<= 、 |= 和字符串函数 strcat、strcpy、strlen
        for (int i = 0, j = 0; i < filelength; i++)
        {
            if (ch == ht[i].value)
            {
                j = (sizeof(ht[i].code)) / 8;//这里可能出错,j值是ht内有多少个数据
                strcat(code, ht[i].code);
                break;
            }
            while (strlen(code) >= 8)
            {
                unsigned char byte = (unsigned char)strtoul(code, NULL, 2);
                code += 8;    // 指向余下的编码
                fwrite(&byte, sizeof(unsigned char), 1, fp);
                compfilelength++; // 压缩文件长度加 1
                remain_bit = 8;    // 写入已经满了一个字节,将剩余位数重置为8位
            }
            // 缓存不足一个字节的部分编码,并记录剩余位数
            remain_bit = remain_bit - strlen(ht[i].code);
            buf <<= strlen(ht[i].code);
            buf |= (unsigned char)strtoul(ht[i].code, NULL, 2);
        }
        // 如果最后剩余的编码不足一个字节,需要用 0 补足一个字节再写入
        if (remain_bit == 0)
        {
            fwrite(&buf, sizeof(unsigned char), 1, fp);
            remain_bit = 8;
            buf = 0;
        }
        // 读取下一个字符
        fread(&ch, sizeof(unsigned char), 1, fp);
        // 处理剩余不足一个字节的编码部分
    }
    if (remain_bit != 8)
        {
            buf <<= remain_bit;    // 缓存中可能存在不足8位的数据
            fwrite(&buf, sizeof(unsigned char), 1, fp);
        }
    
    ///////////////////////////////////
    fclose(fp);
    fclose(cfp);
    free(data);
    free(ht);
    free(code);
    // 用原始文件长度与压缩文件长度的比值作为压缩率
    printf("\n Compression ratio : \t %.2f%% \n\n", ((double)(compfilelength) / filelength) * 100);
    return 1;
}

/*/////////////////////////////////
// 解压文件
// filename:文件名
// compfilename:压缩文件名
// 解压成功返回 1, 失败返回 0
*//////////////////////////////////
int DeCompress(const char* compfilename, const char* filename)
{
    FILE* fp, * cfp;
    int leafnum, nodenum;    // 叶子结点数、全部结点数 nodenum = 2 * leafnum - 1
    ///////////////////////////////////
    cfp = fopen(compfilename, "rb");
    if (cfp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    // 读取叶子结点数,这是压缩时写入的第一个数据
    fread(&leafnum, sizeof(int), 1, cfp);
    int i;
    Data* data;
    data = (Data*)calloc(leafnum, sizeof(Data));    // 存放 值 和 权重
    ///////////////////////////////////
    // 从压缩文件中继续读取构成叶子结点的值域和权重到数组 data
    for (int i = 0;i<leafnum-1; i++)
    {
        fread(&data[i].value,sizeof(unsigned char),1, cfp);
        fread(&data[i].value, sizeof(int), 1, cfp);
    }
    // 还原压缩时使用的 Huffman 树
    HuffmanTree ht;
    nodenum = 2 * leafnum - 1;
    ht = (HuffmanTree)calloc(nodenum, sizeof(HuffmanNode));
    InitHuffmanTree(ht, data, leafnum);
    CreateHuffmanTree(ht, leafnum);
    ///////////////////////////////////
    int filelength;                    // 原文件的长度,以字节为单位
    int root = nodenum - 1, cur;    // 根结点 root 的位置
    unsigned char ch;
    int writenlength = 0;            // 已解压出的字节数,和原文件的 filelength 相等时解压结束
    fread(&filelength, sizeof(int), 1, cfp);    // 读取原始文件的长度
    fp = fopen(filename, "wb");    // 创建原始文件
    if (fp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    cur = root;        // 每次解码都是从 root 开始
    while (1)
    {
        // 从压缩文件件中逐字节读取并解压
        fread(&ch, sizeof(unsigned char), 1, cfp);
        // 根据 ch 中的 8 个二进制位,向下搜索
        // 如果到达了叶子结点,将叶子结点中的 value 写入原始文件中
        // 同时将 writenlength 的值增 1
        // 考虑学习使用位运算运算符 & 、 <<= 
        for (int i = 7; i >= 0; i--)
        {
            if ((1 << i) & ch)
            {
                if (ht[cur].rchild >= 0)
                    cur = ht[cur].rchild;
            }
            else
            {
                if (ht[cur].lchild >= 0)
                    cur = ht[cur].lchild;
            }
            if (ht[cur].lchild == -1 && ht[cur].rchild == -1)
            {
                fputc(ht[cur].value, fp);
                cur = root;
                writenlength++;
            }
        }
        if (writenlength == filelength)
            goto END;// 解压完成
    }
    END:
    fclose(cfp);
    fclose(fp);
    free(data);
    free(ht);
    return 1;
}
int main()
{
    char first;//判断用户选择的功能
    char fileyq[10], fileyh[10], filejq[10], filejh[10];//压缩与解压缩的文件名
    clock_t star,end;
    while (1)
    {
        printf("   ____________________________________________________________\n");
        printf("   |                                                          |\n");
        printf("   | C - 压缩文件                                             |\n");
        printf("   | D - 解压缩                                               |\n");
        printf("   | Q - 退出                                                 |\n");
        printf("   |__________________________________________________________|\n\n\n");
    loop:
        printf("   请选择功能:");
        scanf_s("%c%c", &first);//这里第二个%C用来吃掉回车
        if (first == 'C' || first == 'c')
        {
            printf("   请您输入需要压缩的文件:");
            scanf_s("%s", fileyq, (unsigned)_countof(fileyq));
            printf("\n\n   请您输入压缩后的文件:");
            scanf_s("%s", fileyh, (unsigned)_countof(fileyq));
            printf("\n\n   正在帮您压缩...\n");
            printf("   Compression ratio :    ");
            star = clock();
            CompressFile(fileyq, fileyh);
            end = clock();
            printf("\n\n\n\n");
            printf("   压缩操作完成!\n\n");
            printf("   压缩耗费时间:%lf秒\n\n", (float)(end-star)/CLOCKS_PER_SEC);
        }
        else
        {
            if (first == 'D' || first == 'd')
            {
                printf("   请您输入需要解压缩的文件:");
                scanf_s("%s", filejq,(unsigned)_countof(fileyq));
                printf("\n\n   请您输入解压缩后的文件:");
                scanf_s("%s", filejh, (unsigned)_countof(fileyq));
                printf("\n\n   正在帮您解压缩...\n\n");
                star = clock();
                DeCompress(filejq, filejh);
                end = clock();
                printf("   解压缩操作完成!\n\n");
                printf("   解压缩耗费时间%lf秒\n\n", (float)(end - star) / CLOCKS_PER_SEC);
            }
            else
            {
                if (first == 'Q' || first == 'q')
                {
                    printf("   再见");
                    break;
                }
                else
                {
                    printf("   选项错误,请重新输入\n\n");
                    goto loop;
                }
            }

        }
    }
    return 0;
}


#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <string.h>
#include <conio.h>
#include <limits.h>
// 构造 Huffman 树的数据
// 包含值域和权重
typedef struct _DATA_
{
    unsigned char value;    // 值域
    int weight;                // 权重
}Data;
// Huffman 树结点类型
// 值域、权重、父节点、左孩子、右孩子及 Huffman 编码
// 采用一维数组存储 Huffman 树
// parent, lchild, rchild 取值为结点在数组中的下标
// Huffman 编码采用动态数组
typedef struct _HUFFMANNODE_
{
    unsigned char value;        // 值域
    int weight;                    // 权重
    int parent, lchild, rchild;    // parent , leftchild , rightchild
    char* code;                    //  Huffman 编码
}HuffmanNode, * HuffmanTree;
///////////////////////////////////
// 函数声明
void descendsort(HuffmanNode ht[], int n);
void InitHuffmanTree(HuffmanTree ht, Data data[], int dn);
int CreateHuffmanTree(HuffmanTree ht, int dn);
void CreateHuffmanCode(HuffmanTree ht, int leafnum);
int ScanFile(const char* filename, Data data[], int dn);
int CompressFile(const char* filename, const char* compfilename);
int DeCompress(const char* compfilename, const char* filename);
void Code(HuffmanTree ht, int leafnum, char str[]);
void DeCode(HuffmanTree ht, int leafnum, char code[]);
///////////////////////////////////
/*/////////////////////////////////
// 采用一维数组 ht 存储 Huffman 树
// 对数组 ht 中的前 n 个结点,按权重以 降序 排序
//
// ht:        存放 Huffman 树的一维数组
// 也可表示为  HuffmanTree ht
// void descendsort( HuffmanTree ht, int n )
*//////////////////////////////////
void descendsort(HuffmanNode ht[], int n)
{
    int i, j;
    HuffmanNode temp;
    for (i = 0; i < n - 1; i++)
    {
        for (j = i + 1; j < n; )
        {
            if (ht[i].weight > ht[j].weight)
                j++;
            else
            {
                temp = ht[i];
                ht[i] = ht[j];
                ht[j] = temp;
                j++;
            }
        }
    }
}
/*/////////////////////////////////
// 利用数组 data 中的 dn 个数据
// 对存储在一维数组 ht 中的 Huffman 树进行初始化
// 根据 Huffman 树的性质
// 把前 dn 个结点作为叶子结点,用 data 中的数据对成员 value、weight进行赋值
// 成员 parent 赋值为 0,成员 lchild、rchild 赋值为 -1
//
// dn 个叶子结点的 Huffman 树,共有 2 * dn - 1 个结点
// 对于其它结点,成员 weight、parent赋值为 0
// 成员 lchild、rchild 赋值为 -1
*//////////////////////////////////
void InitHuffmanTree(HuffmanTree ht, Data data[], int dn)
{
    int i;
    for (i = 0; i < dn; i++)
    {
        ht[i].value = data[i].value;
        ht[i].weight = data[i].weight;
        ht[i].parent = 0;
        ht[i].lchild = -1;
        ht[i].rchild = -1;
    }
    for (; i < 2 * dn - 1; i++)
    {
        ht[i].weight = 0;
        ht[i].parent = 0;
        ht[i].lchild = -1;
        ht[i].rchild = -1;
    }
}
/*/////////////////////////////////
// 根据 ht 中的叶子结点,构造带权路径长度(WPL)最小的 Huffman 树
// ht :存放 Huffman 树结点的数组,也可表示为 HuffmanNode ht[]
// int CreateHuffmanTree( HuffmanNode ht[], int dn )
// 叶子结点位于 ht[0] - ht[dn-1]
// 完成后,返回叶子结点的个数 leafnum
*//////////////////////////////////
int CreateHuffmanTree(HuffmanTree ht, int dn)
{
    int leafnum = 0, nodenum; // 叶子结点数、全部结点数 nodenum = 2 * leafnum - 1
    // 调用 descendsort 对前面的 dn 个叶子结点进行排序
    descendsort(ht, dn);
    // 按权重从大到小排序后
    // 从前面的 dn 个叶子结点中,排除权重为0的结点
    // 用 leafnum 记录新的叶子结点数   leafnum <= dn
    for (int i = 0; i < dn; i++)
    {
        if (ht[i].weight == 0)
        {
            break;
        }
        leafnum++;
    }
    nodenum = 2 * leafnum - 1;
    // 根据 ht 中的叶子结点,构造带权路径长度(WPL)最小的 Huffman 树
    // 每次选择两个权重最小、且 parent 为 0 的结点
    // INT_MAX 表示 int 类型的最大值
    int i, min1, min2, x, y;
    // 合并叶子结点
    for (i = leafnum; i < nodenum; i++)
    {
        min1 = min2 = INT_MAX;
        x = y = 0;
        // 在所有 weight 不为 0 的节点中选出 2 个深度最小的
        for (int j = 0; j < i; j++)
        {
            if (ht[j].weight == 0)
            {
                continue;
            }
            if (ht[j].parent == 0)
            {
                if (ht[j].weight < min1)
                {
                    min2 = min1;
                    y = x;
                    min1 = ht[j].weight;
                    x = j;
                }
                else if (ht[j].weight < min2)
                {
                    min2 = ht[j].weight;
                    y = j;
                }
            }
        }
        // 合并节点
        ht[x].parent = i;
        ht[y].parent = i;
        ht[i].lchild = x;
        ht[i].rchild = y;
        ht[i].weight = min1 + min2;
    }
    /////////////////////////////////
    // 以 leafnum 作为返回值
    return leafnum;
}
/*/////////////////////////////////
// 对 Huffman 树中的叶子结点进行编码
// ht:        存放 Huffman 树结点的数组
// leafnum:叶子结点的个数,叶子结点位于 ht[0] - ht[leafnum-1]
// 从每个叶子结点出发,利用 parent 向上直到根结点
// 根结点的标志是其 parent 为 0
// 根据路径进行编码,左 0 右 1
// 编码长度不超过 leafnum
// 需要用 calloc 生成动态数组来保存编码
*//////////////////////////////////
void CreateHuffmanCode(HuffmanTree ht, int leafnum)
{
    int index, len = leafnum;
    char* code;
    code = (char*)calloc(len, sizeof(char)); //  暂时存放一个 Huffman 编码,编码长度不超过 leafnum
    for (int i = 0; i < leafnum; ++i) // 逐个处理叶子结点
    {
        index = len - 1;
        // 由于是从叶子开始,编码按从后向前的顺序生成
        // 例如:编码 "10100" 是按照 0 0 1 0 1 的顺序生成的
        // code: [leafnum-1]   [leafnum-2]     [ ]   [ ]   [ ]   [leafnum-6 ]   ……  [2] [1] [0]
        //         空字符          '0'         '0'   '1'   '0'        '1'
        //                                                      index 为此处的下标值 leafnum-6
        // 最后根据码长创建动态数组  ht[i].code
        // 用 strcpy 将 code 中的编码复制到 ht[i].code 中
        // 此处添加代码
        ht[i].code = (char*)calloc(len, sizeof(char));
        int cur = i, p = ht[cur].parent;
        while (p != 0)
        {
            if (ht[p].lchild == cur)
                code[--index] = '0';
            else
                code[--index] = '1';
            cur = p;
            p = ht[p].parent;
        }
        strcpy_s(ht[i].code, len - index, &code[index]);
    }
    free(code);
}
/*/////////////////////////////////
// 根据 Huffman 树,对 str 中的字符串进行编码并显示
// ht :存放 Huffman 树结点的数组,也可表示为 HuffmanNode ht[]
// leafnum:叶子结点的个数,叶子结点位于 ht[0] - ht[leafnum-1]
// 相应的 Huffman 码在 ht[i].code 中
// str;存放字符串的数组
*//////////////////////////////////
void Code(HuffmanTree ht, int leafnum, char str[])
{
    for (int i = 0; 'a' <= str[i] && str[i] <= 'z'; i++)
    {
        for (int j = 0; j < leafnum; j++)
        {
            if (ht[j].value == str[i])
            {
                printf("%s", ht[j].code);
                break;
            }
        }
    }
    printf("\n");
}
/*/////////////////////////////////
// 根据 Huffman 树进行译码,得到原先的字符串
// ht :存放 Huffman 树结点的数组,也可表示为 HuffmanNode ht[]
// leafnum:叶子结点的个数,叶子结点位于 ht[0] - ht[leafnum-1]
// code;待译码的二进制串
*//////////////////////////////////
void DeCode(HuffmanTree ht, int leafnum, char code[])
{
    int nodenum = 2 * leafnum - 1;    // 总的结点数
    int root = nodenum - 1;            // Huffman 树根结点的下标
    int cur = root;
    // 从根结点开始,根据 code 中的编码左下或右下,直到叶子结点
    // 用 cur 记录当前结点,到达叶结点的条件是 cur < leafnum
    for (int i = 0; i < strlen(code); i++)
    {
        if (code[i] == '0')
            cur = ht[cur].lchild;
        else
            cur = ht[cur].rchild;

        if (cur < leafnum)
        {
            printf("%c", ht[cur].value);
            if (code[i] == '0' || code[i] == '1')
            {
                cur = root;
                continue;
            }
            else
                break;
        }
    }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*/////////////////////////////////
// 扫描文件,统计权重
//
// 利用 Huffman 树进行文件压缩时
// 将构成文件的二进制串
// 按照 8 位一组,分解为字节序列
//
// 以字节为单位读取文件 filename
// 将每个字节值出现的次数作为权重
// 记录在数组 data 中
// 由于一个字节的取值范围为 0 ~ 255 共 256 个值
// data[0].value = 0        data[0].weight 为 0000 0000 在文件中出现的次数
// data[1].value = 1        data[1].weight 为 0000 0001 在文件中出现的次数
// data[255].value = 255    data[255].weight 为 1111 1111 在文件中出现的次数
// 参数 dn 为数组 data 的长度
// 在调用函数时被传值为 256
//
*//////////////////////////////////
int ScanFile(const char* filename, Data data[], int dn)
{
    FILE* fp;
    fp = fopen(filename, "rb");
    if (fp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    // 对 data[i].value 和 data[i].weight 赋初始值
    for (int i = 0; i < 256; i++)
    {
        data[i].value = i;
        data[i].weight = 0;
    }
    ///////////////////////////////////
    // 用 fread 从文件中每次读取一个字节
    // 统计每个字节所取值的出现频率
    // 同时记录文件的长度
    int filelength = 0;    // 文件长度,以字节为单位
    unsigned char ch;
    int m=fread(&ch, sizeof(unsigned char), 1, fp);
    while(m==1)
    {
        data[ch].weight++;
        filelength++;
        m=fread(&ch, sizeof(unsigned char), 1, fp);
    }
    fclose(fp);
    // 返回文件的长度,以字节为单位
    return filelength;
}

/*/////////////////////////////////
// 利用 Huffman 树进行文件压缩时
// 将构成文件的二进制串
// 按照 8 位一组,分解为字节序列
// 由于一个字节的取值范围为 0 ~ 255 共 256 个值
// 将每个字节值出现的次数作为权重
// 记录在长度为 256 的数组 data 中
// filename:        原文件名
// compfilename:    压缩文件名
// 压缩成功,返回  1      压缩失败,返回 0
*//////////////////////////////////
int CompressFile(const char* filename, const char* compfilename)
{
    int valuenum = 256;
    int filelength;        // 以字节为单位的文件长度
    int leafnum;        // 叶子结点数
    Data* data;            // 存放数据的值和权重
    HuffmanTree ht;
    ///////////////////////////////////
    // 不同于英文字母的权重,压缩文件时,需要通过扫描文件进行统计,得到权重数据
    data = (Data*)calloc(valuenum, sizeof(Data));        // valuenum 为 256
    // 扫描待压缩的文件,统计每个字节值的频率
    // 同时统计文件长度,以字节为单位
    filelength = ScanFile(filename, data, valuenum);
    if (0 == filelength) return 0;        // 扫描失败
    ht = (HuffmanTree)calloc(2 * valuenum - 1, sizeof(HuffmanNode));    // 结点总数不会超过 2*valuenum-1
    // 初始化顺序存储的 Huffman 树结点
    InitHuffmanTree(ht, data, valuenum);
    // 创建 Huffman 树
    leafnum = CreateHuffmanTree(ht, valuenum); // leafnum <= valuenum 有些值可能在文件中未出现
    //生成 Huffman 编码
    CreateHuffmanCode(ht, leafnum);
    // 测试代码,显示 Huffman 编码,完成后删除
    for (int i = 0; i < leafnum; i++)
    {
        printf("[ %3d ] : %s\n", ht[i].value, ht[i].code);
        if ((i + 1) % 32 == 0) _getch();
    }//////////////////////////////////
    ///////////////////////////////////
    // 生成压缩文件
    int i;
    FILE* fp, * cfp;
    int compfilelength = 0;  // 压缩后文件的长度
    // 首先将叶子结点数、每个叶子结点的值和权重、文件原始长度依次写入压缩文件
    ///////////////////////////////////
    cfp = fopen(compfilename, "wb");    // 新建二进制文件并写入
    if (cfp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    fwrite(&leafnum, sizeof(int), 1, cfp);    // 写入叶子结点数
    // 此处补充代码,写入其它数据
    for (i = 0; i < leafnum; i++)
    {
        fwrite(&ht[i].value, sizeof(char), 1, cfp);
        fwrite(&ht[i].weight, sizeof(int), 1, cfp);
    }
    fwrite(&filelength, sizeof(int), 1, cfp);
    // 开始对文件进行压缩,需要再次扫描原始文件
    fp = fopen(filename, "rb");
    if (fp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    ///////////////////////////////////
    unsigned char ch;    // 每次写入一个字节
    char* code;
    code = (char*)calloc(2 * valuenum, sizeof(char));    // 存放 Huffman 编码
    unsigned char buf = 0;    // 缓存当前正在拼接的字节
    int remain_bit = 8;    // 跟踪当前字节缓存中剩余的未拼接位数
    fread(&ch, sizeof(unsigned char), 1, fp);        // 从原文件中读取第一个字节
    while (!feof(fp))
    {
        // 根据 ch 的值找到对应的叶子结点
        // 用叶子结点的 Huffman 编码,取代 ch 写入压缩文件中
        // 难点在于,写入必须以字节为单位,而每个编码的长度并不是字节的整数倍
        // 例如某个编码为 9 位,需要先把前 8 位作为一个字节写入
        // 剩下的 1 位和后面的编码凑够一个字节后再次写入
        // 还要注意,编码是字符串,"11110000"是8个字节
        // 要变为二进制数 11110000 后才是一个字节
        // 考虑学习使用位运算运算符 <<= 、 |= 和字符串函数 strcat、strcpy、strlen
        for (int i = 0, j = 0; i < filelength; i++)
        {
            if (ch == ht[i].value)
            {
                j = (sizeof(ht[i].code)) / 8;//这里可能出错,j值是ht内有多少个数据
                strcat(code, ht[i].code);
                break;
            }
            while (strlen(code) >= 8)
            {
                unsigned char byte = (unsigned char)strtoul(code, NULL, 2);
                code += 8;    // 指向余下的编码
                fwrite(&byte, sizeof(unsigned char), 1, fp);
                compfilelength++; // 压缩文件长度加 1
                remain_bit = 8;    // 写入已经满了一个字节,将剩余位数重置为8位
            }
            // 缓存不足一个字节的部分编码,并记录剩余位数
            remain_bit = remain_bit - strlen(ht[i].code);
            buf <<= strlen(ht[i].code);
            buf |= (unsigned char)strtoul(ht[i].code, NULL, 2);
        }
        // 如果最后剩余的编码不足一个字节,需要用 0 补足一个字节再写入
        if (remain_bit == 0)
        {
            fwrite(&buf, sizeof(unsigned char), 1, fp);
            remain_bit = 8;
            buf = 0;
        }
        // 读取下一个字符
        fread(&ch, sizeof(unsigned char), 1, fp);
        // 处理剩余不足一个字节的编码部分
    }
    if (remain_bit != 8)
        {
            buf <<= remain_bit;    // 缓存中可能存在不足8位的数据
            fwrite(&buf, sizeof(unsigned char), 1, fp);
        }

    ///////////////////////////////////
    fclose(fp);
    fclose(cfp);
    free(data);
    free(ht);
    free(code);
    // 用原始文件长度与压缩文件长度的比值作为压缩率
    printf("\n Compression ratio : \t %.2f%% \n\n", ((double)(compfilelength) / filelength) * 100);
    return 1;
}

/*/////////////////////////////////
// 解压文件
// filename:文件名
// compfilename:压缩文件名
// 解压成功返回 1, 失败返回 0
*//////////////////////////////////
int DeCompress(const char* compfilename, const char* filename)
{
    FILE* fp, * cfp;
    int leafnum, nodenum;    // 叶子结点数、全部结点数 nodenum = 2 * leafnum - 1
    ///////////////////////////////////
    cfp = fopen(compfilename, "rb");
    if (cfp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    // 读取叶子结点数,这是压缩时写入的第一个数据
    fread(&leafnum, sizeof(int), 1, cfp);
    int i;
    Data* data;
    data = (Data*)calloc(leafnum, sizeof(Data));    // 存放 值 和 权重
    ///////////////////////////////////
    // 从压缩文件中继续读取构成叶子结点的值域和权重到数组 data
    for (i = 0;i<leafnum-1; i++)
    {
        fread(&data[i].value,sizeof(unsigned char),1, cfp);
        fread(&data[i].value, sizeof(int), 1, cfp);
    }
    // 还原压缩时使用的 Huffman 树
    HuffmanTree ht;
    nodenum = 2 * leafnum - 1;
    ht = (HuffmanTree)calloc(nodenum, sizeof(HuffmanNode));
    InitHuffmanTree(ht, data, leafnum);
    CreateHuffmanTree(ht, leafnum);
    ///////////////////////////////////
    int filelength;                    // 原文件的长度,以字节为单位
    int root = nodenum - 1, cur;    // 根结点 root 的位置
    unsigned char ch;
    int writenlength = 0;            // 已解压出的字节数,和原文件的 filelength 相等时解压结束
    fread(&filelength, sizeof(int), 1, cfp);    // 读取原始文件的长度
    fp = fopen(filename, "wb");    // 创建原始文件
    if (fp == NULL)
    {
        printf("\nERROR: No such file\n");
        return 0;
    }
    cur = root;        // 每次解码都是从 root 开始
    while (1)
    {
        // 从压缩文件件中逐字节读取并解压
        fread(&ch, sizeof(unsigned char), 1, cfp);
        // 根据 ch 中的 8 个二进制位,向下搜索
        // 如果到达了叶子结点,将叶子结点中的 value 写入原始文件中
        // 同时将 writenlength 的值增 1
        // 考虑学习使用位运算运算符 & 、 <<=
        for (i = 7; i >= 0; i--)
        {
            if ((1 << i) & ch)
            {
                if (ht[cur].rchild >= 0)
                    cur = ht[cur].rchild;
            }
            else
            {
                if (ht[cur].lchild >= 0)
                    cur = ht[cur].lchild;
            }
            if (ht[cur].lchild == -1 && ht[cur].rchild == -1)
            {
                fputc(ht[cur].value, fp);
                cur = root;
                writenlength++;
            }
        }
        if (writenlength == filelength)
            goto END;// 解压完成
    }
    END:
    fclose(cfp);
    fclose(fp);
    free(data);
    free(ht);
    return 1;
}
int main()
{
    char first;//判断用户选择的功能
    char fileyq[10], fileyh[10], filejq[10], filejh[10];//压缩与解压缩的文件名
    clock_t star,end;
    while (1)
    {
        printf("   ____________________________________________________________\n");
        printf("   |                                                          |\n");
        printf("   | C - 压缩文件                                             |\n");
        printf("   | D - 解压缩                                               |\n");
        printf("   | Q - 退出                                                 |\n");
        printf("   |__________________________________________________________|\n\n\n");
    loop:
        printf("   请选择功能:");fflush(stdout);
        rewind(stdin);scanf("%c", &first);
        if (first == 'C' || first == 'c')
        {
            printf("   请您输入需要压缩的文件:");
            scanf_s("%s", fileyq, (unsigned)_countof(fileyq));
            printf("\n\n   请您输入压缩后的文件:");
            scanf_s("%s", fileyh, (unsigned)_countof(fileyq));
            printf("\n\n   正在帮您压缩...\n");
            printf("   Compression ratio :    ");
            star = clock();
            CompressFile(fileyq, fileyh);
            end = clock();
            printf("\n\n\n\n");
            printf("   压缩操作完成!\n\n");
            printf("   压缩耗费时间:%lf秒\n\n", (float)(end-star)/CLOCKS_PER_SEC);
        }
        else
        {
            if (first == 'D' || first == 'd')
            {
                printf("   请您输入需要解压缩的文件:");
                scanf_s("%s", filejq,(unsigned)_countof(fileyq));
                printf("\n\n   请您输入解压缩后的文件:");
                scanf_s("%s", filejh, (unsigned)_countof(fileyq));
                printf("\n\n   正在帮您解压缩...\n\n");
                star = clock();
                DeCompress(filejq, filejh);
                end = clock();
                printf("   解压缩操作完成!\n\n");
                printf("   解压缩耗费时间%lf秒\n\n", (float)(end - star) / CLOCKS_PER_SEC);
            }
            else
            {
                if (first == 'Q' || first == 'q')
                {
                    printf("   再见");
                    break;
                }
                else
                {
                    printf("   选项错误,请重新输入\n\n");
                    goto loop;
                }
            }

        }
    }
    return 0;
}


以下仅供参考:

#include <iostream>
#include <string>
using namespace std;
struct huffTree {
    int parent;
    int lchild;
    int rchild;
    int weight;
    string flag;
};
struct Lowest_node {
    char ch;
    int ch_num;
};
void coding(int length,huffTree *tree,int n,int &a,int &b) {
    int i;
    int r,s;

    r=s=length;
    for (i=0;i<n;i++) {
        if (tree[i].weight<r
         && tree[i].parent==-1) {
            r=tree[i].weight;
            a=i;
        }
    }
    for (i=0;i<n;i++) {
        if (tree[i].weight<s
         && i!=a
         && tree[i].parent==-1) {
            s=tree[i].weight;
            b=i;
        }
    }
}
void frequency(string str) {
    int i,j;
    int length=str.length();
    Lowest_node *node=new Lowest_node[length];

    for (i=0;i<length;i++) node[i].ch_num=0;

    int char_type_num=0;
    for (i=0;i<length;i++) {
        for (j=0;j<char_type_num;j++)
            if (str[i]==node[j].ch
            || ('a'<=node[j].ch && node[j].ch<='z'
                && str[i]+32==node[j].ch))
                break;//
        if (j<char_type_num) node[j].ch_num++;
        else {
            if ('A'<=str[i] && str[i] <= 'Z') node[j].ch=str[i]+32;
            else node[j].ch=str[i];
            node[j].ch_num++;
            char_type_num++;
        }
    }
    for (i=0;i<char_type_num;i++) {
        for (j=i;j<char_type_num;j++) {
            if (node[j].ch_num<node[j+1].ch_num) {
                int temp;
                char ch_temp;
                temp=node[j].ch_num;
                ch_temp=node[j].ch;
                node[j].ch_num=node[j+1].ch_num;
                node[j].ch=node[j+1].ch;
                node[j+1].ch_num=temp;
                node[j+1].ch=ch_temp;
            }
        }
    }
    for (i=0;i<char_type_num;i++)
        cout<<"字符"<<node[i].ch<<"出现了"<<node[i].ch_num<<"次"<<endl;
    huffTree *huff=new huffTree[2*char_type_num-1];
    huffTree temp;
    string *code=new string[2*char_type_num-1];

    for (i=0;i<2*char_type_num-1;i++) {
        huff[i].lchild=-1;
        huff[i].parent=-1;
        huff[i].rchild=-1;
        huff[i].flag=-1;
    }
    for (j=0;j<char_type_num;j++) huff[j].weight=node[j].ch_num;
    int min1,min2;
    for (int k=char_type_num;k<2*char_type_num-1;k++) {
        coding(length,huff,k,min1,min2);
        huff[min1].parent=k;
        huff[min2].parent=k;
        huff[min1].flag="0";
        huff[min2].flag="1";
        huff[k].lchild=min1;
        huff[k].rchild=min2;
        huff[k].weight=huff[min1].weight+huff[min2].weight;
    }
    for (i=0;i<char_type_num;i++) {
        temp=huff[i];
        while (1) {
            code[i]=temp.flag+code[i];
            temp=huff[temp.parent];
            if (temp.parent==-1) break;//
        }
    }
    cout<<"字符串的每个字符huffman编码为:"<<endl;
    for (i=0;i<char_type_num;i++) cout<<node[i].ch<<"  "<<code[i]<<endl;
    cout<<"整个字符串的huffman编码为:"<<endl;
    for (i=0;i<length;i++) {                                                                                     //S?
        for (j=0;j<char_type_num;j++) {
            if (str[i]==node[j].ch)
                cout<<code[j];
        }
    }
    delete[] node;
    node=NULL;
    delete[] huff;
    huff=NULL;
    delete[] code;
    code=NULL;
}
int main() {
    int length=0;
    string str;
    cout<<"请输入一个字符串:";
    cin>>str;
    frequency(str);
    return 0;
}
//请输入一个字符串:2333abcde
//字符3出现了3次
//字符2出现了1次
//字符a出现了1次
//字符b出现了1次
//字符c出现了1次
//字符d出现了1次
//字符e出现了1次
//字符串的每个字符huffman编码为:
//3  11
//2  000
//a  001
//b  010
//c  011
//d  100
//e  101
//整个字符串的huffman编码为:
//000111111001010011100101