#include<stdio.h>
#include<stdlib.h>
这两行是头文件包含,stdio.h
提供了输入输出函数,stdlib.h
提供了内存分配等函数。
typedef struct Node{
char x;
int num;
struct Node* next;
}N;
定义了一个名为Node
的结构体,并使用typedef
为其取别名N
。这个结构体用于表示链表中的节点,包含一个字符x
、一个表示出现次数的整数num
以及指向下一个节点的指针next
。
typedef struct TreeNode{
char x;
int num;
struct TreeNode* next;
struct TreeNode* right;
struct TreeNode* left;
}TN;
定义了一个名为TreeNode
的结构体,并使用typedef
为其取别名TN
。这个结构体用于表示哈夫曼树中的节点,包含一个字符x
、一个表示出现次数的整数num
、指向左右子节点的指针left
和right
以及指向下一个节点的指针next
typedef struct path{
char x;
struct path* next;
}P;
定义了一个名为path
的结构体,并使用typedef
为其取别名P
。这个结构体可能用于表示哈夫曼编码中的路径,包含一个字符x
和指向下一个路径节点的指针next
。
TN** link2=NULL;
N* link=NULL;
int num=0;
这里声明了三个全局变量。link2
是一个指向TreeNode
指针的指针,初始化为NULL
,可能用于指向一个动态分配的数组,存储哈夫曼树的节点。link
是一个指向Node
结构体的指针,初始化为NULL
,可能用于指向一个链表的头部。num
是一个整数,初始化为 0,可能用于记录link2
所指向的数组中的节点数量。
void m1(char x){
if(link!=NULL){
N* flag=link;
while(flag!=NULL){
if(flag->x==x){
flag->num++;
return;
}else{
flag=flag->next;
}
}
}
N* w=(N*)malloc(sizeof(N));
w->x=x;
w->num=1;
w->next=link;
link=w;
}
这个函数m1
接收一个字符参数x
。如果link
不为NULL
,则遍历由link
指向的链表,查找是否存在字符为x
的节点。如果找到,则增加该节点的出现次数并返回。如果没有找到,则创建一个新的节点,将字符x
和出现次数 1 存入新节点,并将新节点插入到链表的头部。
void showlink(){
N* flag=link;
while(flag!=NULL){
printf("节点值%c,出现次数%d\n",flag->x,flag->num);
flag=flag->next;
}
}
这个函数用于遍历并打印由link
指向的链表中的每个节点的字符和出现次数。
void showlink2(){
TN** flag=link2;
int k=0;
for(;k<num;k++){
printf("节点值%c,出现次数%d\n",flag[k]->x,flag[k]->num);
}
}
这个函数用于遍历并打印由link2
指向的数组中的每个节点的字符和出现次数。
void moveTolink2(){
N* temp=link;
num=0;
while(temp!=NULL){
num++;
temp=temp->next;
}
link2=(TN**)malloc(sizeof(TN)*num);
N* flag=link;
int index=0;
for(;flag!=NULL;index++){
TN* w=(TN*)malloc(sizeof(TN));
w->x=flag->x;
w->num=flag->num;
w->right=NULL;
w->left=NULL;
link2[index]=w;
flag=flag->next;
}
}
这个函数用于将由link
指向的链表中的节点转移到由link2
指向的数组中。首先统计链表中的节点数量并更新num
,然后根据num
动态分配一个数组的内存空间。接着遍历链表,为每个节点创建一个新的TreeNode
结构体,并将其复制到数组中。
TN* getTwo(){
int min1=link2[0]->num;
int k=1;
int index1=0;
for(;k<num;k++){
TN* w=link2[k];
if(w->num<=min1){
min1=w->num;
index1=k;
}
}TN* w=link2[index1];
int s=index1;
for(;s<num;s++){
link2[s]=link2[s+1];
}
num--;
return w;
}
这个函数用于从由link2
指向的数组中找到并返回两个出现次数最小的节点之一。首先找到数组中出现次数最小的节点的索引,然后将该节点从数组中移除(通过将后面的节点向前移动一位,并更新num
),最后返回该节点的指针。
void merge(){
TN* x1=getTwo();
TN* x2=getTwo();
TN* x3=(TN*)malloc(sizeof(TN));
x3->num=x1->num+x2->num;
x3->left=x1;
x3->right=x2;
link2[num]=x3;
num++;
while(num!=1){
merge();
}
}
这个函数用于构建哈夫曼树。它首先调用getTwo
函数两次,获取两个出现次数最小的节点,然后创建一个新的节点,将新节点的出现次数设置为两个子节点出现次数之和,将新节点的左右子节点分别设置为获取的两个节点。接着将新节点添加到由link2
指向的数组中,并更新num
。然后通过递归调用自身,继续合并节点,直到数组中只剩下一个节点(即哈夫曼树的根节点)。
void showTree(TN* xx,P* path){
printf("节点num值%d,字符值%c\n",xx->num,xx->x);
if(xx->left!=NULL){
P* l=(P*)malloc(sizeof(P));
l->x='0';
l->next=path;
P* r=(P*)malloc(sizeof(P));
r->x='1';
r->next=path;
showTree(xx->left,l);
showTree(xx->right,r);
}else{
printf("路径是:");
while(path!=NULL){
printf("%c",path->x);
path=path->next;
}
printf("\n");
}
}
这个函数用于打印哈夫曼树中的节点信息和对应的哈夫曼编码路径。如果当前节点有子节点,则为左右子节点分别创建一个新的路径节点,并递归调用自身打印子节点的信息和路径。如果当前节点没有子节点(即叶节点),则打印该节点的哈夫曼编码路径。
int main(int argc,char *argv[]){
char* str="shjlshglkshadhfjssdfdsgksjfdkjhhdfg";
int k=0;
for(;;k++){
if(str[k]=='\0'){
break;
}else{
m1(str[k]);
}
}
在main
函数中,定义了一个字符串str
,然后通过一个无限循环遍历这个字符串。对于每个字符,调用m1
函数处理该字符,将其添加到由link
指向的链表中(如果字符已经存在,则增加出现次数)。当遇到字符串结束符时,退出循环。
showlink();
printf("=========\n");
moveTolink2();
showlink2();
打印由link
指向的链表中的节点信息,然后调用moveTolink2
函数将链表中的节点转移到由link2
指向的数组中,并打印数组中的节点信息。
merge();
printf("哈夫曼树根节点%d\n",link2[0]->num);
showTree(link2[0],NULL);
调用merge
函数构建哈夫曼树,然后打印哈夫曼树的根节点的出现次数,并调用showTree
函数打印哈夫曼树的节点信息和对应的哈夫曼编码路径。
return 0;
}
main
函数返回 0,表示程序正常结束。