二叉树的经典应用——哈夫曼树

1、问题描述:给定n个字符的权值数组w,根据哈夫曼编码与译码规则,实现一个哈夫曼编/译码系统。

2、利用顺序表存储及实现Huffman树,编码结果直接输出,要求利用栈,引入以前所写的栈实现函数。

算法逻辑:

①首先,定义一个存储哈夫曼树的数组HT[MAX],使用顺序存储结构;定义一个整型数n,用于控制输入的字符和对应的权值的数量;使用getchar()吸收回车;

②定义一个CreatHuffmanTree函数,用于构建哈夫曼树;并输出哈夫曼树每个节点的信息;定义一个Select函数,用于选择两个权值最小的节点,帮助构建哈夫曼树;

③构建完哈夫曼树之后,定义一个CreatHuffmanCode函数,用于创建哈夫曼编码;使用栈stack存储和输出每个字符对应的哈夫曼编码;

④定义一个整型数nn,用于控制输入的要译码的字符数量;使用getchar()吸收回车;

⑤定义一个TranslateHuffmanCode函数,用于译码;并输出哈夫曼编码对应的字符;

⑥程序结束。

源代码:

HuffmanTree.cpp文件:

#include<stdio.h>
#include<string.h>
#define MAX 100
typedef char ElemType;
#include"sqstack.h" //用栈把哈夫曼编码输出
typedef struct {
  ElemType data; //data存储的数据为字符型
  int weight; //weight是权值
  int lchild,rchild,parent; //左孩子,右孩子,双亲节点
} HuffmanTree;
//选择两个权值最小的节点,并将他们的位置存在s1和s2中
void Select(HuffmanTree *HT,int n,int *s1,int *s2){
  int min1=n,min2=n; //min1是权值最小,min2是权值次小,初始化为n
  int i;
  for (i=1;i<=n;i++){
    if (HT[i].parent==-1){ //首先判断该节点是否已经被连接过
      if (HT[i].weight<=HT[min1].weight){ //比较权值
        min1=i;
      }
    }
  }
  for (i=1;i<=n;i++){
  //剔除min1,并判断该节点是否已经被连接过
    if (HT[i].parent==-1 && i!=min1){
      if (HT[i].weight<=HT[min2].weight){ //比较权值
        min2=i;
      }
    }
  }
  (*s1)=min1;//利用指针将节点的位置传回去
  (*s2)=min2;
}
int m; //m为构造哈夫曼树需要的所有节点数
void CreatHuffmanTree(HuffmanTree *HT,int n){ //构造哈夫曼树
  if (n<=1) return ; //若只有一个节点,函数结束
  int i;
  m=2*n-1; //原节点有n个,需再新增n-1个
  for (i=1;i<=m;i++){
    HT[i].data='*'; //将所有节点初始化
    HT[i].lchild=-1;
    HT[i].rchild=-1;
    HT[i].parent=-1;
  }
  for (i=1;i<=n;i++){ //输入给定的数据
    scanf("%c",&HT[i].data); //输入字符
    getchar(); //吸收空格
    scanf("%d",&HT[i].weight); //输入对应的权值
    getchar(); //吸收回车
  }
  int s1,s2; //用来存Select函数的选择结果
  for (i=n+1;i<=m;i++){ //构造哈夫曼树需创建新节点,从n+1开始
    Select(HT,i-1,&s1,&s2); //从已创建的节点中选出两个权值最小的
    HT[s1].parent=i; //将这两个节点连接到新节点
    HT[s2].parent=i;
    HT[i].lchild=s1; //新节点的左孩子为权值最小的节点
    HT[i].rchild=s2; //右孩子为权值次小的节点
    HT[i].weight=HT[s1].weight+HT[s2].weight;//新节点的权值为两个节点之和
  }
  printf("\n哈夫曼树:\n");
  printf("(从左到右分别为:字符,位置,权值,左孩子,右孩子,双亲节点)\n");
  for (i=1;i<=m;i++){ //将构造好的哈夫曼树输出
    printf("%c,%d,%d,%d,%d,%d\n",HT[i].data,i,
    HT[i].weight,HT[i].lchild,HT[i].rchild,HT[i].parent);
  }
  printf("\n");
}

void CreatHuffmanCode(HuffmanTree *HT,int n){ //创建哈夫曼编码
  Stack S; //定义顺序栈S用于存储和输出哈夫曼编码
  InitStack(&S); //初始化栈
  int i,p,l;
  for (i=1;i<=n;i++){ //从第一个叶子节点开始编码
    p=HT[i].parent; //p用于存放当前叶子节点的双亲节点的位置
    l=i; //l存放当前叶子节点位置
    while (p!=-1){ //p不为根节点就执行循环体,将节点往前推
      //判断当前叶子节点的双亲节点p的左孩子是不是当前叶子节点l
      if (HT[p].lchild==l){
        Push(&S,'0'); //若是,将字符0入栈
      }else {
        Push(&S,'1'); //若不是,则为右孩子,将字符1入栈
      }
      l=p; //往前一个推
      p=HT[p].parent;
    }
    printf("%c的哈夫曼编码:",HT[i].data);
    char ch; //存放出栈的字符
    while (!StackEmpty(S)){ //将栈中的所有01字符输出
      Pop(&S,&ch);
      printf("%c,",ch);
    }
    printf("\n");
  }
}
//哈夫曼译码
void TranslateHuffmanCode(HuffmanTree *HT,int nn){
  char c[MAX]; //存放要译码的字符串
  int i;
  for (i=1;i<=nn;i++){
    scanf("%c",&c[i]); //输入要译码的字符串
  }
  getchar();
  int q=m; //p存放根节点的位置
  printf("译码结果:\n");
  for (i=1;i<=nn;i++){

    if (c[i]=='0'){ //0往左边走
      q=HT[q].lchild;
      if (HT[q].lchild==-1){ //若左孩子为-1是叶子节点,即找到
        printf("%c,",HT[q].data);
        q=m; //从根节点重新开始
      }
    }
    else if (c[i]=='1'){ //1往右边走
      q=HT[q].rchild;
      if (HT[q].lchild==-1){ //若左孩子为-1是叶子节点,即找到
        printf("%c,",HT[q].data);
        q=m; //从根节点重新开始
      }
    }
    else { //非0和1的字符忽略
      continue;
    }
  }
}

int main(){
  HuffmanTree HT[MAX]; //创建存储哈夫曼树的数组
  int n;
  printf("请输入要编码的字符数量n\n");
  scanf("%d",&n);
  getchar(); //吸收回车
  printf("请输入每个字符和对应的权值n\n");
  CreatHuffmanTree(HT,n); //构建哈夫曼树
  CreatHuffmanCode(HT,n); //创建哈夫曼编码
  int nn;
  printf("\n请输入要译码的字符数量nn\n");
  scanf("%d",&nn);
  getchar(); //吸收回车
  printf("请输入要译码的字符\n");
  TranslateHuffmanCode(HT,nn); //译码
  printf("\n\n程序结束");

  return 0;
}

sqstack.h文件:

#define MAXSIZE 100
typedef struct {
  ElemType data[MAXSIZE];//存放数据元素 数据元素类型不同的应用有不同的类型
  int top; //栈顶指针
} Stack;

//初始化
//功能 空表 last=-1
//单向传递
void InitStack(Stack *S){
  S->top = -1;
}

//在线性表中第i个位置插入元素e
int Push(Stack *S,ElemType e)
{
  if(S->top+1==MAXSIZE){
    printf("stack overflow!\n");
    return 0;
  }
  S->top++;
  S->data[S->top]=e;
}

ElemType GetTop(Stack S)
{
  return S.data[S.top];
}

int StackEmpty(Stack S)
{
  if(S.top==-1)
    return 1;
  else
    return 0;
}

int Pop(Stack *S,ElemType *e)
{
  if(StackEmpty(*S))
    return 0;
  *e = S->data[S->top];
  S->top--;
  return 1;
}    

 

posted @   ulyee  阅读(93)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示