記事のディレクトリ
ハフマンコード
このコードを書くのは難しいことではありませんが、非常に面倒で、書くのに4時間以上かかりました。。。
(それはあなた自身の食べ物です、あなたはコードを上手に書く能力を持っていません)
- 関連情報
- タイトル:ハフマンコード
- 説明:おばあちゃんチェンユエMOOC
- アイデアは単純ですが、これは実際には実装が簡単ではなく、ビデオはアイデアを提供するだけです。特定のコードは自分で完成させる必要があります。もちろん、データ構造ビデオには、コードを提供する実装ビデオもたくさんあります。 。
- しかし、注意深く見ると、この質問の最小ヒープは、ビデオの一部の数値ではなく、ノード上に直接構築する必要があることがわかります。
ヘッドファイル
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
構造の定義
- 最小のヒープ構造
- ハフマンツリー構造
#define MINDATA -1
typedef struct TreeNode* HuffmanTree;
typedef struct HNode* Heap; /* 堆的类型定义 */
typedef Heap MinHeap; /* 最小堆 */
// 堆用数组实现 Huffman用树实现
struct HNode {
HuffmanTree* Data; /* 存储元素的数组 */
int Size; /* 堆中当前元素个数 */
int Capacity; /* 堆的最大容量 */
};
struct TreeNode {
int Weight;
HuffmanTree Left;
HuffmanTree Right;
};
最小ヒープの確立
最大のヒープの確立について書きましたが、ヒープがわからない場合は参照してください。
- CreateHeapはヒープを作成します
- ペアの挿入は、ノードをヒープに挿入します
- ReadDataはいくつかの数値を読み取り、それらをノードに変換して、対応する頻度を保存します
- DeleteMinは、ヒープから最小のノードを削除します
- BuildMinHeapは、取得されたノードをヒープし、それらをヒープに配置します
MinHeap CreateHeap(int Size)
{
/* 创建容量为Size的空的最小堆 */
MinHeap H = (MinHeap)malloc(sizeof(struct HNode));
H->Data = (HuffmanTree*)malloc((Size + 1) * sizeof(HuffmanTree));
for (int i = 0; i <= Size; i++) {
H->Data[i] = (HuffmanTree)malloc(sizeof(struct TreeNode));
}
H->Size = 0;
H->Capacity = Size;
H->Data[0]->Weight = MINDATA; /*定义"哨兵",为小于堆中所有可能元素的值*/
return H;
}
bool IsFull(MinHeap H)
{
return (H->Size == H->Capacity);
}
bool Insert(MinHeap H, HuffmanTree T)
{
/* 将元素X插入最小堆H,其中H->Data[0]已经定义为哨兵 */
int i;
if (IsFull(H)) {
printf("FULL");
return false;
}
i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
for (; H->Data[i / 2]->Weight > T->Weight; i /= 2) {
H->Data[i] = H->Data[i / 2]; /* 上滤X */ // 当比父结点的值小时,让父结点的值下到子结点上
}
H->Data[i] = T; /* 将X插入 */
return true;
}
MinHeap ReadData(MinHeap H, int N, int* fre) {
char c; int f;
for (int i = 1; i < N + 1; i++) {
// 执行N次
scanf("%c %d", &c, &f);
getchar();
fre[i - 1] = f;
H->Data[i]->Weight = f;
H->Data[i]->Left = NULL; H->Data[i]->Right = NULL;
H->Size++;
}
return H;
}
int IsEmpty(MinHeap H) {
return H->Size == 0;
}
/*取出来最小值(堆顶)*/
HuffmanTree DeleteMin(MinHeap H) {
// 把最后一个结点放到堆顶然后调整
if (IsEmpty(H)) {
printf("EMPTY\n");
return NULL;
}
int Parenet, Child;
HuffmanTree X;
HuffmanTree Minitem;
Minitem = H->Data[1]; // 返回最小值
X = H->Data[H->Size--]; // 保存最后一个结点的值并删除一个结点
// 开始调整堆 找左子树与右子树最小值 进行与X的比较
for (Parenet = 1; Parenet * 2 <= H->Size; Parenet = Child) {
Child = Parenet * 2;
if ((Child != H->Size) && (H->Data[Child]->Weight > H->Data[Child + 1]->Weight)) {
// 有右结点 且 右结点值小
Child++; //Child 指向左右结点最小的值
}
if (X->Weight <= H->Data[Child]->Weight) // 元素比子结点最小值还小时则找到正确的位置
break;
else
H->Data[Parenet] = H->Data[Child];
}
H->Data[Parenet] = X;
return Minitem;
}
void PercDown(MinHeap H, int p)
{
/* 下滤:将H中以H->Data[p]为根的子堆调整为最小堆 */
int Parent, Child;
HuffmanTree X;
X = H->Data[p]; /* 取出根结点存放的值 */
for (Parent = p; Parent * 2 <= H->Size; Parent = Child) {
Child = Parent * 2;
if ((Child != H->Size) && (H->Data[Child]->Weight > H->Data[Child + 1]->Weight)) // 右结点的值小
Child++; /* Child指向左右子结点的较小者 */
if (X->Weight <= H->Data[Child]->Weight) break; /* 找到了合适位置 */
else /* 下滤X */
H->Data[Parent] = H->Data[Child]; // 讲子结点比父结点小的值放在父结点上
}
H->Data[Parent] = X;
}
void BuildMinHeap(MinHeap H)
{
/* 调整H->Data[]中的元素,使满足最小堆的有序性 */
int i;
/* 从最后一个结点的父节点开始,到根结点1 */
for (i = H->Size / 2; i > 0; i--)
PercDown(H, i);
}
最小のヒープを使用して、独自のハフマンツリーを構築します
- 最小のヒープから2つの最小のノードを見つけます
- 新しいノードを使用して2つのノードを子ノードとして接続し、新しいノードの重みは2つの子ノードの合計です
- 次の使用のために、ソートのために新しいノードを最小のヒープに配置します
HuffmanTree Huffman(MinHeap H) {
int i; HuffmanTree T;
BuildMinHeap(H);
int Size = H->Size;
for (i = 1; i < Size; i++) {
// 一共进行Size-1次
T = (HuffmanTree)malloc(sizeof(struct TreeNode)); // 构建一个Huffman树
T->Left = DeleteMin(H);
T->Right = DeleteMin(H);
T->Weight = T->Left->Weight + T->Right->Weight;
//printf("add weight = %d %d %d\n", T->Weight, T->Left->Weight, T->Right->Weight);
Insert(H, T);
}
T = DeleteMin(H);
return T;
}
構築したハフマンツリーのWPLを計算します
int WPL(HuffmanTree T, int Depth) {
if (!T->Left && !T->Right) {
// 为叶结点
return (Depth * T->Weight);
} // 否则定有两个子结点
return (WPL(T->Left, Depth + 1) + WPL(T->Right, Depth + 1));
}
質問の学生によって提出されたコードの構築と削除について
- 各文字の生徒のエンコーディングに従ってツリーを作成し、各生徒の入力後にツリーを削除します
- ツリーを構築した後、彼のコードがプレフィックスコードであるかどうかを判断します
int BulidTree(HuffmanTree* T, char* s) {
// 用测试的code建一个树 建树错误时返回1
HuffmanTree p = *T;
int num = strlen(s);//计算字符串长度
for (int i = 0; i < num; i++) {
//printf("i = %d\n", i);
if (s[i] == '0') {
if (!p->Left) {
// 如果子结点不存在
p->Left = (HuffmanTree)malloc(sizeof(struct TreeNode));
p->Left->Weight = 0;
p->Left->Left = NULL;
p->Left->Right = NULL;
}
else if(p->Left->Weight == 1){
// 说明这个地方有放编码的叶结点了,但是还要基于这个叶结点往下建立结点 --- 这个编码必定是错误的
return 1;
}
p = p->Left;
}
else if (s[i] == '1') {
if (!p->Right) {
// 如果子结点不存在
p->Right = (HuffmanTree)malloc(sizeof(struct TreeNode));
p->Right->Weight = 0;
p->Right->Left = NULL;
p->Right->Right = NULL;
}
else if (p->Right->Weight == 1) {
// 说明这个地方有放编码的叶结点了,但是还要基于这个叶结点往下建立结点 --- 这个编码必定是错误的
return 1;
}
p = p->Right;
}
}
//printf("*********");
if (p->Left || p->Right) {
// 如果一个编码放到最后发现他不是叶结点 --- 必定是错误的
return 1;
}
p->Weight = 1;
return 0;
}
void DeleteTree(HuffmanTree T) {
if (T->Left)
DeleteTree(T->Left);
else if (T->Right)
DeleteTree(T->Right);
free(T);
}
学生の意見を確認する
- まず、生徒の各キャラクターのコードを読みます
- ツリーを構築して、プレフィックスコードかどうかを判断します
- それが最小のコードであるかどうかを判断します(上記で計算された自作のハフマンツリーによって計算されたWPLを学生の施設と比較します)
int main() {
int N;
scanf("%d", &N);
getchar();
int* fre = (int*)malloc(sizeof(int) * N);
MinHeap H = CreateHeap(N);
H = ReadData(H, N, fre); // 仅读入数据
HuffmanTree T = Huffman(H);
int CodeLen = WPL(T, 0);
从此往下为对学生输入的检验
int M;
scanf("%d", &M);
getchar();
char aa;
char* s = (char*)malloc(sizeof(char) * (N - 1)); // 保存code
for (int i = 0; i < M; i++) {
int is_huff = 1; // 1 代表正确
int Len = 0;
HuffmanTree T = (HuffmanTree)malloc(sizeof(struct TreeNode));
T->Weight = 0; // 正确测试编码建立的数中,叶结点的weigth为1 其他为0
T->Left = NULL; T->Right = NULL;
for (int j = 0; j < N; j++) {
scanf("%c %s", &aa, s);
getchar();
//printf("j = %d is_huff = %d s = %s aa = %c\n", j, is_huff, s, aa);
if (is_huff != 0) {
// 当目前给的代码正确时才执行
int num = strlen(s);// 计算字符串长度
if (num > N - 1) {
// 得到的Huffman编码最长为N-1
is_huff = 0;
}
Len += num * fre[j]; // 得到的编码长度乘以编码次数
//printf("num = %d fre = %d\n", num, fre[j]);
if(BulidTree(&T, s)){
// 建树错误时返回1
is_huff = 0;
}
}
//printf("is_huffend = %d\n", is_huff);
}
//printf("len = %d", Len);
if (Len != CodeLen) {
is_huff = 0;
}
//printf("is_huffend1en = %d\n", is_huff);
if (is_huff)
printf("Yes\n");
else
printf("No\n");
DeleteTree(T);
}
}