Huffman Codes (30分)【C语言】(Huffman树的操作练习)

习题讲解视频

题目:

In 1953, David A. Huffman published his paper “A Method for the Construction of Minimum-Redundancy Codes”, and hence printed his name in the history of computer science. As a professor who gives the final exam problem on Huffman codes, I am encountering a big problem: the Huffman codes are NOT unique. For example, given a string “aaaxuaxz”, we can observe that the frequencies of the characters ‘a’, ‘x’, ‘u’ and ‘z’ are 4, 2, 1 and 1, respectively. We may either encode the symbols as {‘a’=0, ‘x’=10, ‘u’=110, ‘z’=111}, or in another way as {‘a’=1, ‘x’=01, ‘u’=001, ‘z’=000}, both compress the string into 14 bits. Another set of code can be given as {‘a’=0, ‘x’=11, ‘u’=100, ‘z’=101}, but {‘a’=0, ‘x’=01, ‘u’=011, ‘z’=001} is NOT correct since “aaaxuaxz” and “aazuaxax” can both be decoded from the code 00001011001001. The students are submitting all kinds of codes, and I need a computer program to help me determine which ones are correct and which ones are not.

输入格式

Each input file contains one test case. For each case, the first line gives an integer N (2≤N≤63), then followed by a line that contains all the N distinct characters and their frequencies in the following format:
c[1] f[1] c[2] f[2] … c[N] f[N]
where c[i] is a character chosen from {‘0’ - ‘9’, ‘a’ - ‘z’, ‘A’ - ‘Z’, ‘_’}, and f[i] is the frequency of c[i] and is an integer no more than 1000. The next line gives a positive integer M (≤1000), then followed by M student submissions. Each student submission consists of N lines, each in the format:
c[i] code[i]
where c[i] is the i-th character and code[i] is an non-empty string of no more than 63 '0’s and '1’s.

输出格式

For each test case, print in each line either “Yes” if the student’s submission is correct, or “No” if not.
Note: The optimal solution is not necessarily generated by Huffman algorithm. Any prefix code with code length being optimal is considered correct.

输入样例

7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11

输出样例

Yes
Yes
No
No

算法

对输入的测试用例,完成以下两个条件测试关键算法描述

最优编码

  • 建立Huffman树
  • 计算WPL值

无歧义解码(前缀码)

  • 根据编码建树,路径中经过的结点检查标记位是否为零
  • 编码的最后一位必须停在叶节点(对最后一位的左右指针进行检查,必须全为空)

代码实现

/*StructArray记录输入标准数组*/
typedef struct{
	char Character;
	int Frequence;
}StructArray;
/*Struct记录输入比对数组*/
typedef struct{
	char Character;
	char Str[70];
}Struct; 
int main()
{	
	int N;
	scanf("%d\n",&N);
	StructArray* A=(StructArray*)malloc(sizeof(StructArray)*(N+1));
	int i=1;
	for(i=1;i<=N;i++){
	    scanf(" %c %d",&(A[i].Character),&(A[i].Frequence));
	}
	HuffmanTree Tree=CreateHuffmanTree(A,N);
	int wpl=CalculateWPL(Tree,0);
	int K;
	scanf("%d\n",&K);
	Struct* B=(Struct*)malloc(sizeof(Struct)*(N+1));
	int j=1;
	for(j=0;j<K;j++){
	    for(i=1;i<=N;i++){
	        scanf("\n%c %s",&(B[i].Character),(B[i].Str));
	    }
	    /*B权值和sum*/
	    int sum=0;
	    {
	        int l=0,Character='0',r=0;
	        for(i=1;i<=N;i++){
	            Character=B[i].Character;
	            l=strlen(B[i].Str);
	            for(r=1;r<=N;r++){
	                if( Character == A[r].Character ){
	                    sum+=A[r].Frequence*l;
	                    break;
	                }
	            }
	        }
	    }
	    /*判断*/ 
	    if(sum!=wpl){
	        printf("No\n");
	    }else{
	        int ret=Judge(B,N); 
	        if(ret==0){
	            printf("No\n");
	        }else{
	            printf("Yes\n");
	        }
	    }
	}
	free(A);
	return 0;
}
		

最小堆常见操作

typedef struct HNode *Heap; /* 堆的类型定义 */
struct HNode {
	ElementType *Data; /* 存储元素的数组 */
	int Size;          /* 堆中当前元素个数 */
	int Capacity;      /* 堆的最大容量 */
};
typedef Heap MinHeap; /* 最小堆 */
#define MINDATA -1000  /* 该值应根据具体情况定义为小于堆中所有可能元素的值 */
MinHeap CreateHeap( int Size )
{ /* 创建容量为Size的空的最小堆 */
	MinHeap H = (MinHeap)malloc(sizeof(struct HNode));
	H->Data = (ElementType*)malloc((Size+1)*sizeof(ElementType));
	H->Size = 0;
	H->Capacity = Size;
	H->Data[0].Data = MINDATA; /* 定义"哨兵"为小于堆中所有可能元素的值*/
return H;
}
bool IsFull( MinHeap H )
{
	return (H->Size == H->Capacity);
}
bool Insert( MinHeap H, ElementType X )
{ /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
	int i;
	if ( IsFull(H) ) { 
	    printf("最小堆已满");
	    return false;
	}
	i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
	for ( ; H->Data[i/2].Data > X.Data; i/=2 )
	    H->Data[i].Data = H->Data[i/2].Data; /* 上滤X */
	H->Data[i] = X; /* 将X插入 */
	return true;
}
bool IsEmpty( MinHeap H )
{
	return (H->Size == 0);
}
ElementType* DeleteMin( MinHeap H )
{ /* 从最小堆H中取出键值最小元素另存到内存空间,返回对应指针 */
	int Parent, Child;
	ElementType X;
	ElementType* MinItem=(ElementType*)malloc(sizeof(ElementType));
	if ( IsEmpty(H) ) {
	    printf("最小堆已为空");
	    return NULL;
	}
	*MinItem = H->Data[1]; /* 取出根结点存放的最大值 */
	/* 用最小堆中最后一个元素从根结点开始向上过滤下层结点 */
	X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
	for( Parent=1; Parent*2<=H->Size; Parent=Child ) {
	    Child = Parent * 2;
	    if( (Child!=H->Size) && (H->Data[Child].Data>H->Data[Child+1].Data) )
	        Child++;  /* Child指向左右子结点的较大者 */
	    if( X.Data <= H->Data[Child].Data ) break; /* 找到了合适位置 */
	    else  /* 下滤X */
	        H->Data[Parent] = H->Data[Child];
	}
	H->Data[Parent] = X;
	return MinItem;
} 
/*---------------------- 建造最小堆 ----------------------*/
void PercDown( MinHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最小堆 */
	int Parent, Child;
	ElementType X;
	X = H->Data[p]; /* 取出根结点存放的值 */
	for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
	    Child = Parent * 2;
	    if( (Child!=H->Size) && (H->Data[Child].Data>H->Data[Child+1].Data) )
	        Child++;  /* Child指向左右子结点的较大者 */
	    if( X.Data <= H->Data[Child].Data ) break; /* 找到了合适位置 */
	    else  /* 下滤X */
	        H->Data[Parent] = H->Data[Child];
	}
	H->Data[Parent] = X;
}
void BuildHeap( MinHeap H )
{ /* 调整H->Data[]中的元素,使满足最小堆的有序性  */
	/* 这里假设所有H->Size个元素已经存在H->Data[]中 */
	int i;
	/* 从最后一个结点的父节点开始,到根结点1 */
	for( i = H->Size/2; i>0; i-- )
	    PercDown( H, i );
} 

Huffman树操作

 /*Huffman树结点*/ 
typedef struct Node* TreeNode;
struct Node{
	int Data; 
	TreeNode Left;
	TreeNode Right;
};
typedef TreeNode HuffmanTree;
HuffmanTree CreateHuffmanTree(StructArray A[],int Size)
{
	MinHeap Heap=CreateHeap( Size );
	int i=0;
	for(i=1;i<=Size;i++){
	    Heap->Size++;
	    Heap->Data[i].Data=A[i].Frequence;
	    Heap->Data[i].Left=NULL;
	    Heap->Data[i].Right=NULL;
	}
	BuildHeap(Heap);
	for(i=1;i<Size;i++){
	    struct Node node;
	    TreeNode left=DeleteMin(Heap);
	    TreeNode right=DeleteMin(Heap);
	    node.Left=left;
	    node.Right=right;
	    node.Data=left->Data+right->Data;
	    Insert(Heap,node);
	}
	return DeleteMin(Heap);
}
void DestroyHuffmanTree(HuffmanTree T)
{
	if(T->Left==NULL&&T->Right==NULL){
	    free(T);
	    return ;
	}
	if(T->Left){
	    DestroyHuffmanTree(T->Left);
	}
	if(T->Right){
	    DestroyHuffmanTree(T->Right);
	}
}		

前缀码判别

int Judge(Struct B[],int N)
{
	int flag=-1; 
	int i=0,j=0;
	char str[70]="/0";
	int l=0; 
	typedef struct _Tree *Tree;
	struct _Tree{
	    int Flag;
	    Tree Left;
	    Tree Right;
	};
	void DestroyTree(Tree T)
	{
	    if(T->Left){
	        DestroyTree(T->Left);
	    }
	    if(T->Right){
	        DestroyTree(T->Right);
	    }
	    if(T->Left==NULL&&T->Right==NULL){
	        free(T);
	        return;
	    }
	}
	Tree root=(Tree)malloc(sizeof(struct _Tree));
	root->Flag=0;
	root->Left=NULL;
	root->Right=NULL;
	for(i=1;i<=N;i++){
	    strcpy(str,B[i].Str);
	    l=strlen(B[i].Str);
	    Tree add=root;
	    int r=0;
	    for(r=0;r<l;r++){
	        int m=str[r]-'0';
	        if(m==0){
	            if(add->Left){
	                if(add->Left->Flag==1){
	                    flag=0;
	                    goto end;
	                }else{
	                    add=add->Left;
	                }
	            }else{
	                Tree Temp=(Tree)malloc(sizeof(struct _Tree));
	                Temp->Flag=0;
	                Temp->Left=NULL;
	                Temp->Right=NULL;
	                add->Left=Temp;
	                add=Temp;
	            }
	        }else{
	            if(add->Right){
	                if(add->Right->Flag==1){
	                    flag=0;
	                    goto end;
	                }else{
	                    add=add->Right;
	                }
	            }else{
	                Tree Temp=(Tree)malloc(sizeof(struct _Tree));
	                Temp->Flag=0;
	                Temp->Left=NULL;
	                Temp->Right=NULL;
	                add->Right=Temp;
	                add=Temp;
	            }
	        }
	        if(r==l-1){
	            add->Flag=1;
	            if(add->Left||add->Right){
	                flag=0;
	                goto end; 
	            } 
	        } 
	    }
	} 
	end:
	    DestroyTree(root);
	    if(flag==0){
	        return 0;
	    }else{
	        return 1;
	    }
}

WPL

int CalculateWPL(HuffmanTree Tree,int Depth)
{
	if(Tree->Left==NULL&&Tree->Right==NULL)return Tree->Data*Depth;
	return CalculateWPL(Tree->Left,Depth+1)+CalculateWPL(Tree->Right,Depth+1);
}  

难点总结

Huffman树的构造中,所用的堆是数组存储形式而Huffman树使用链表存储形式。
可以把堆的数组元素定义成Huffman树的结构结点形式。

动态内存的释放时机

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值