哈夫曼编码器

  • 程序介绍
  • 程序整体设计思想
  • 程序详解
    • 数据结构
    • 动态存储
    • 静态存储
    • 编码译码
      • 编码核心思想
      • 核心代码
      • 译码核心思想
      • 核心代码
    • 哈夫曼树可视化
      • 核心思想
      • 核心代码
  • 完整代码
    • 头文件
    • main函数
  • 程序
    • 程序部分截图
    • 程序演示

程序介绍

  1. 设计一个程序,实现哈夫曼树编码,译码,动态可视化,友好界面的功能
  2. 程序中用到了Easy-X图形库
  3. 本程序使用vs2015编写

程序整体设计思想

【main函数】
(1) 定义栈,用来保存译码,因为译码过程是自底向上的,输出就要把他反过来。
(2) 定义hfmnode类型的head指针和root指针(用静态和动态之分),head指针是初始的有序链表的头指针,root是建好的哈夫曼树的根指针。
(3) 算静态存储和动态储存下的结点坐标
(4) 显示操作界面
(5) 根据选择执行操作
【执行步骤】
(1)点击第一个选项,进入哈夫曼树可视化界面,选择它的存储方式,执行可视化。
(2)点击第二个选项,进入译码界面,选择它的存储方式,进行译码。译码在执行过程中如果译码的字符太多,输出会进行换行。
(3)点击第三个选项,进入编码界面,选择它的存储方式,进行编码。同样,输入过多的会,在画布上面输出也会换行。
(4)点击第四个选项,进入程序介绍界面。
(5)点击第五个选项,显示谢幕画面,推出程序。

程序详解

数据结构

1. 逻辑结构:线性结构
2. 存储结构:线性存储,链式存储
3. 结构体:存储坐标,存储树结点,用栈存储编码

结构体如下:

typedef struct//存储结点xy坐标
{double x;double y;
}point;
typedef struct node
{int data;//权值----频度char leaf;//字符double id;//对应完全二叉树的id值,每个结点不一样point p;//一个结点的坐标struct node* parent;//双亲int ltag, rtag;//用来编码,左0右1int tag;//标记struct node *lchild, *rchlid;//哈夫曼树中左右孩子指针struct node *next;//用于动态
}hfmnode;
typedef struct//用来保存编码
{int A[MAX];hfmnode B[MAX];int top;int tag[MAX];//用来后序遍历树
}stack;

动态存储

  1. 代码设计思想:

定义hfmnode head,root,用sort函数从文件中依次读取数据,并进行排序,按由小到大的顺序存入一个单向链表。用head指针指向地址带入creathfm(head)函数中创建哈弗曼树。每执行一次,产生一个新结点,带入insert重新排序,新结点的左右孩子分别指向原来的两个结点,原两个结点的双亲指针指向新结点,重复执行操作直至root指针的next为空。返回的root即为所创建哈夫曼树的根结点指针。

静态存储

  1. 代码设计思想:

定义hfmnode A1[MAX]数组,用sort_1函数从文件中依次读取数据,并进行排序,按由小到大的顺序存入哈夫曼数组。用head_1指针指向A1[0]地址带入creathfm_1(head_1, A1)函数中创建哈弗曼树。取数组中最小的两个结点,结点值相加成新结点保存于数组中,新结点的左右孩子分别指向原来的两个结点,原两个结点的双亲指针指向新结点,重复以上操作直至root指针的next为空。返回的root即为所创建哈夫曼树的根结点指针。Coding函数进行左0右1标记。静态创建哈夫曼树完成

编码译码

编码核心思想

编码,即:字符转化为01代码
(1) 将每个结点进行标记; 左孩子不为空,则将ltag标记为0,右孩子不为空,则将rtag标记为1。叶子结点则都标记为-1;用coding(hfmnode *root) 函数实现。
(2)查找所要编码字符的位置; 递归进行查找,就是二叉树查找结点的操作;用find(hfmnode *root, char s)函数实现。
(3)将编码入栈保存;从要编码字符的位置往上执行,直到根节点为止。如果某个结点是他双亲的左孩子,就把它的左标记0入栈。反之,就将1入栈。用code(hfmnode *root, stack *st, char s) 函数执行。
(4)编码字符串;根据输入的字符串从后往前进行依次编码。因为每一个字符的编码都是倒序的。用printcode(hfmnode *root, stack *st)函数实现。

核心代码

hfmnode* coding(hfmnode *root)
{hfmnode *p1;p1 = root;if (p1->lchild == NULL&&p1->rchlid == NULL)//叶子结点的左右标记置为-1{p1->ltag = p1->rtag = -1;}else//不是叶子结点,就左0右1标记{if (p1->lchild != NULL){p1->ltag = 0;}if (p1->rchlid != NULL){p1->rtag = 1;}coding(p1->lchild);coding(p1->rchlid);}return root;
}
hfmnode* find(hfmnode *root, char s)//查找s结点的位置,因为后面编码的过程是从底往上的
{                               //就是二叉树的查找结点操作;hfmnode *p, *p1;p = root;if (root == NULL)return NULL;else if (root->leaf == s)return root;else{p1 = find(root->lchild, s);if (p1)return p1;elsereturn find(root->rchlid, s);}}
void code(hfmnode *root, stack *st, char s)//编码---自底向上
{hfmnode *p1, *pre;p1 = find(root, s);//找到要编码字符的位置while (p1->parent != NULL)//从叶子往上找,直到找到跟结点,根节点的双亲结点为空{pre = p1->parent;if (pre->lchild == p1)//如果p1是他双亲的左孩子,就把它的左标记0入栈。{                   //因为编码是自叶子结点到跟结点的顺序,最后的编码就是倒序的,当然 也可以用数组来存,但是用栈的话分高一点。push(st, root->ltag);}else if (pre->rchlid == p1){push(st, root->rtag);}p1 = p1->parent;}
}
void printcode(hfmnode *root, stack *st)//输出编码   //A--065    B--090  " "--032
{char a[MAX];int n = 0;int i = 0;printf("请输入您想要编码的字符:");gets_s(a);while (a[i] != '\0'){n = n + 1;i++;}n = n - 1;//i = 0;while (n >= 0){code(root, st, a[n]);//把上面倒序的编码再倒序输出,它就变成正序了。n--;}
}

译码核心思想

译码,即:01代码转化成字符串
从根节点往下译码;根据输入的01代码,是0就往左孩子走,反之就往右孩子走,直到找到一个对应的字符,然后使指针重新指向根节点进行译码下一个。如果没有找到对应的字符,就输出#。用decoding(hfmnode *root, char *str)函数实现。

核心代码

void decoding(hfmnode *root, char *str)//译码
{char a[MAX];printf("请输入您需要译码的字符串:");scanf_s("%s", a, MAX);int i = 0;//i用于对记录下一个翻译编码的位置int j = 0;//j用于下一个保存字符的位置hfmnode *p1;p1 = root;while (a[i] != '\0') //从根结点向下译码{while (p1->lchild)//翻译出一个字符{if (a[i] == '0') //0向左孩子{p1 = p1->lchild;}else if (a[i] == '1') //1向右孩子{p1 = p1->rchlid;}elsebreak;  //不符合条件就跳出循环i++;}if (p1->leaf == '#'){str[j++] = '#';//编码没有对应译码}elsestr[j++] = p1->leaf; //编码有对应译码p1 = root;//重新从根向下找}str[j] = '\0';//设置结束标志
}

哈夫曼树可视化

核心思想

········传统的哈夫曼树图形化结点会交叉或重叠,或可以用二叉树的显示方法:括号表示或凹入法表示,但那样不直观。所以,可以在传统哈夫曼树的图形化上做一改进,改进之后,不管哈夫曼树的结点有多少,他都不会出现交叉或重叠的现象。并且这样的哈夫曼树具有层次分明,排序有致,结点间的路径永远不会相交的优点。
········将一棵哈夫曼树较友好的可视化出来。即:不能有交叉和重叠
通过以下步骤:

(1)将哈夫曼树中每个结点都对应到一棵完全二叉树中,为每个结点编号为id。即根节点的id为1,他的左孩子的id为他的二倍,他的右孩子为他id的二倍加1。直到每个结点都有他的id值。
(2)改变所有叶子结点的id值,用公式计算每个叶子结点的id值,算出来这个值会在[1,2)之间。把叶子结点放到一个辅助数组A中,这样方便后面算叶子的横坐标值,然后再赋值给树中的叶子结点,因为直接给到树中的叶子比较麻烦,所有上面才用到一个存叶子结点的辅助数组A;
(3)根据叶子的id值,算出每个叶子结点的横坐标的值。即:根据一个叶子的id值在所有叶子id值的次序,给他一个横坐标值,先给到数组中存的叶子节点。然后找到叶子的位置,把上面放到数组中的结点的横坐标值赋值给树中叶子的p.x。
(4)根据叶子的id值算双亲结点的横坐标值。双亲结点的横坐标值等于他子女横坐标值相加除以2,----数学中两点中点的坐标公式。通过后序来实现,因为后序是先子女后根。
(5)在根据公式,这个公式算出来就是一个结点的层数,然后根据层数算出每个结点的纵坐标值。叶子结点的id已经改变,不能这样算,所以他的纵坐标值为他双亲纵坐标值加上一个值。
(6)最后用非递归后序输出这棵哈夫曼树。

核心代码

void getid(hfmnode *root)//把哈夫曼树的每个结点对应到一棵完全二叉树中,给每个结点一个id值。
{                        //即根节点的id为1,他的左孩子的id为他的二倍,他的右孩子为他id的二倍加1//用递归来实现if (root->lchild != NULL){root->lchild->id = 2 * root->id;//他的左孩子的id为他双亲id的二倍root->rchlid->id = 2 * root->id + 1;//他的右孩子的id为他双亲id的二倍加1getid(root->lchild);//往左getid(root->rchlid);//往右}
}
void getleafid(hfmnode *root)//改变叶子的id值,用一个公式,公式在文档中
{if (root->lchild == NULL){root->id = root->id / pow(2, int(log(root->id) / log(2)));//根据公式改idA[mo++] = *root;//把叶子结点放到A数组中root->tag = 1;//给叶子标记为1}else{getleafid(root->lchild);//用递归来实现getleafid(root->rchlid);}
}
void getleaf_x(hfmnode *root)//根据叶子的id值算出叶子的横坐标值,
{                            //然后根据叶子的横坐标值往上算其他结点的横坐标值int n = mo;               //根据叶子的id值在所有叶子中的排序,给他一个横坐标值int i, j;int k;for (i = 0; i < n; i++){k = 1;for (j = 0; j < n; j++){if (A[j].id < A[i].id)k=k+1;}A[i].p.x = 50 + k * 40;//给每个叶子一个横坐标值,先给到刚才放叶子的数组中,后面再赋值给树中的叶子结点//因为直接给到树中的叶子比较麻烦,所有上面才用到一个存叶子结点的辅助数组A;hfmnode *p1 = find(root, A[i].leaf);//找到叶子的位置,把上面在数组中得到的横坐标值赋值给树中叶子的p.xp1->p.x = A[i].p.x;}
}
void getnode_x(hfmnode *root)//根据孩子结点的横坐标值算双亲结点的横坐标,
{                           //双亲结点的横坐标值等于他子女横坐标值相加除以2,----数学中两点中点的坐标公式if (root)             //通过后序来实现,因为后序是先子女后根{getnode_x(root->lchild);getnode_x(root->rchlid);if (root->tag == 1)//是叶子结点就返回,因为叶子结点的横坐标已经算出来了return;else{root->p.x = (root->lchild->p.x + root->rchlid->p.x) / 2;//中点坐标公式root->tag = 1;//把每个算出来坐标的非叶子结点结点标记为1}}
}
void getnode_y(hfmnode *root)//根据每个结点的id值算纵坐标,叶子单独算,因为叶子的id值已经改变
{hfmnode *p1 = root;if (p1){if (p1->lchild == NULL)//叶子纵坐标单独算,他的纵坐标是在他双亲纵坐标上偏移一个值p1->p.y = p1->parent->p.y + 60;else{p1->p.y = 60 * int((log(p1->id) / log(2)) + 1);//非叶子结点的纵坐标值用他的层数乘以每层的高度。getnode_y(p1->lchild);                         //(log(p1->id) / log(2)) + 1就是每个结点的层数getnode_y(p1->rchlid);                           //到此,每个结点的横坐标和纵坐标的值都已经算出来了,//下面用递归或非递归后序来画出每个结点}}
}
void postorder_f(hfmnode *root)
{stack S;S.top = 0;while (root || S.top != 0){if (root)//但root不指向空时,把他入栈,并将这个结点的tag标记为0,表示他没有被访问过{S.B[S.top] = *root;//入栈S.tag[S.top] = 0;S.top++;root = root->lchild;//先往左走,直到叶子结点}else//到叶子结点{if (S.tag[S.top - 1] == 1)//已经被访问过但还没有出栈,这次就出栈{S.top--;root = &S.B[S.top];//让根节点在指向这个点setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 3);setlinecolor(GREEN);setfillcolor(RED);circle(root->p.x, root->p.y, 12);setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 1);settextcolor(RED);char s = root->leaf;if (s != '#'){setbkmode(TRANSPARENT);outtextxy(root->p.x - 5, root->p.y - 7, s);}mciSendString(L"close jpmusic", 0, 0, 0);jpmusic();Sleep(150);if (root->lchild){line(root->p.x, root->p.y + 12, root->lchild->p.x, root->lchild->p.y - 12);settextcolor(RGB(0, 255, 255));char s1 = '0';outtextxy((root->p.x + root->lchild->p.x) / 2 - 15, (root->p.y + root->lchild->p.y) / 2 - 14, s1);line(root->p.x, root->p.y + 12, root->rchlid->p.x, root->rchlid->p.y - 12);char s2 = '1';outtextxy((root->p.x + root->rchlid->p.x) / 2 + 13, (root->p.y + root->rchlid->p.y) / 2 - 14, s2);setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 1);}root = NULL;}else//没有被访问过{root = &S.B[S.top - 1];S.tag[S.top - 1] = 1;root = root->rchlid;}}}
}

完整代码

头文件

#include<stdio.h>
#include<stdlib.h>
#include<graphics.h>
#include<conio.h>
#include<math.h>
#include<Windows.h>
#pragma comment(lib,"Winmm.lib")
#define MAX 5000
#define width 1200
#define height 720TCHAR D_name[] = _T("动态存储");
TCHAR S_name[] = _T("静态存储");
void winsD()
{// 获得窗口句柄HWND hWnd = GetHWnd();// 使用 API 函数修改窗口名称SetWindowText(hWnd, D_name);
}
void winsS()
{// 获得窗口句柄HWND hWnd = GetHWnd();// 使用 API 函数修改窗口名称SetWindowText(hWnd, S_name);
}
typedef struct//存储结点xy坐标
{double x;double y;
}point;
typedef struct node
{int data;//权值----频度char leaf;//字符double id;//对应完全二叉树的id值,每个结点不一样point p;//一个结点的坐标struct node* parent;//双亲int ltag, rtag;//用来编码,左0右1int tag;//标记struct node *lchild, *rchlid;//哈夫曼树中左右孩子指针struct node *next;//用于动态
}hfmnode;
typedef struct//用来保存编码
{int A[MAX];hfmnode B[MAX];int top;int tag[MAX];//用来后序遍历树
}stack;//栈--存译码
void init(stack *st)//初始化栈
{st->top = 0;
}
void push(stack *st, int s)//入栈
{if (st->top == MAX){printf("栈已满,请先清空栈\n");}else{st->A[st->top] = s;st->top++;}
}//动态建树
hfmnode* insert(hfmnode* root, hfmnode* s)//更新有序的哈夫曼结点序列,即:将新产生的
{hfmnode *p1, *p2;if (root == NULL)//只有一个结点的哈夫曼树{s->next = NULL;root = s;}else{p1 = NULL;p2 = root;//用p2指针来找新结点要插入的位置while (p2&&p2->data <= s->data){p1 = p2;//找到位置,并用P1代替p2指向这个位置p2 = p2->next;//p2就是指向他该插入位置的下一个位置}s->next = p2;//把新结点插入到p2之前if (p1 == NULL)//说明新结点的位置在之前有序链表的首部{root = s;}else//在有序链表的中间{p1->next = s;}}return root;
}
hfmnode* creathfm(hfmnode *root1)//由下面sort函数传进来一个有序链表(升序)。
{hfmnode *s, *rl, *rr;hfmnode *root = root1;while (root && (root->next))//每次执行取当前链表的前两个(取完就删除这两个)做兄弟,构建新结点{                            //并把新结点加入链表并保持有序,用上面的insert函数保持有序rl = root;//取当前链表第一个结点rr = root->next;//取第二个root = rr->next;//删除前两个s = (hfmnode*)malloc(sizeof(hfmnode));//创建一个新结点代表刚才取下来的两个结点s->next = NULL;s->data = rl->data + rr->data;//这个新结点的权值等于刚才取下来那两个结点的权值之和s->lchild = rl;//把取下来的第一个结点做这个新结点的左孩子s->rchlid = rr;s->leaf = '#';rl->parent = rr->parent = s;//前两个结点的父亲是新结点的数据rl->next = rr->next = NULL;root = insert(root, s);//把这个新结点加入有序链表}root->parent = NULL;return root;
}
hfmnode* sort()//类似与后插建立有序链表
{hfmnode *head, *s;FILE *fp;fp = fopen("data.txt", "r");s = (hfmnode*)malloc(sizeof(hfmnode));fscanf_s(fp, "%c ", &s->leaf, 1);fscanf_s(fp, "%d ", &s->data);s->tag = 0;s->lchild = s->rchlid = s->next = NULL;//必须置空,不然后序操作无法进行head = NULL;while (s->leaf != '@'){if (head == NULL){head = s;}else if (s->data <head->data)//等于的往后插,保持排序的稳定{s->next = head;head = s;}else{hfmnode *p2, *pre;pre = p2 = head;//和上面insert函数里面保持链表有序的操作一样while (p2&&p2->data <= s->data){pre = p2;p2 = p2->next;}pre->next = s;s->next = p2;}s = (hfmnode*)malloc(sizeof(hfmnode));fscanf_s(fp, "%c ", &s->leaf, 1);fscanf_s(fp, "%d ", &s->data);s->tag = 0;s->next = NULL;s->lchild = s->rchlid = NULL;}fclose(fp);return head;
}//静态建树
void sort_1(hfmnode *a)   //将文件中的结点按data值(整形)大小排序,有序保存于结构体数组中
{FILE *fp1;fp1 = fopen("data.txt", "r");int s = 0, n = 0, p, i;hfmnode x;fscanf_s(fp1, "%c ", &x.leaf, 1);fscanf_s(fp1, "%d ", &x.data);x.tag = 0;x.parent = NULL;x.lchild = x.rchlid = x.next = NULL;//根据data值大小排序,使无序文件内容有序保存在结构体数组中while (x.leaf != '@'){if (s == 0)                   //原数组为空{a[s] = x;}else if (x.data<a[0].data)   //插入最前方{n = s;while (n){a[n] = a[n - 1];n--;}a[0] = x;}else if (a[s - 1].data <= x.data)  //插入最后方 {a[s] = x;}else                             //插入中间{n = p = 0;while (x.data >= a[n].data){p = n;n++;}if (x.data == a[n].data){p = n;n++;}i = s;while (i>n)  //将a[n]后的结点后移,空出a[n]用于保存当前x{a[i] = a[i - 1];i--;}a[n] = x;}n = 0;while (n != s)//为hfmnode中的next赋值,建立连续关系 {a[n].next = &a[n + 1];n++;}a[s].next = NULL;//最后一个结点的next置空//为下一次保存做准备s++;fscanf_s(fp1, "%c ", &x.leaf, 1);fscanf_s(fp1, "%d ", &x.data);x.tag = 0;x.parent = NULL;x.lchild = x.rchlid = x.next = NULL;}}
hfmnode* insert_1(hfmnode* root, int s, hfmnode a[])//对新产生的结点根据data值进行排序
{hfmnode *p1, *p2;if (root == NULL)//只有一个结点的哈夫曼树{a[s].next = NULL;root = &a[s];}else{p1 = NULL;p2 = root;//用p1、p2指针来找新结点要插入的位置,即新结点插入p1、p2之间while (p2&&p2->data <= a[s].data){p1 = p2;//用p1保存目标位置的前一个结点地址p2 = p2->next;//p2为目标位置的后一个结点地址}a[s].next = p2;//把新结点插入到p2之前if (p1 == NULL)//新结点的位置在首部{root = &a[s];}else//新结点在中间或最后{p1->next = &a[s];}}return root;  //返回根结点
}
hfmnode* creathfm_1(hfmnode *root, hfmnode a[])//由上面sort函数传进来一个有序数组(升序)。
{hfmnode *r1, *rr;int s = 0;while (a[s].next != NULL){s++;}s = s + 1;  //a[s]为数组中下一个要保存结点的位置while (root && (root->next))//每次执行取前两个做兄弟(取完根结点后移不再使用这两个结点),构建新结点{                            //并把新结点加入数组并保持next有序,用上面的insert函数保持有序r1 = root;//取当前链表第一个结点rr = root->next;//取第二个root = rr->next;//删除前两个a[s].next = NULL;a[s].data = r1->data + rr->data;//这个新结点的权值等于刚才取下来那两个结点的权值之和a[s].lchild = r1;//第一个结点做新结点的左孩子a[s].rchlid = rr;//第二个结点做新结点的右孩子a[s].leaf = '#';  //非叶子结点的结点值r1->parent = rr->parent = &a[s];//取用两个结点的父亲是新结点r1->next = rr->next = NULL;root = insert_1(root, s, a); //从root开始对后续结点进行排序,为选取最小两个结点做准备s++;}root->parent = NULL;//根结点的双亲结点置空return root;
}//编码译码和界面
//生成编码
hfmnode* coding(hfmnode *root)
{hfmnode *p1;p1 = root;if (p1->lchild == NULL&&p1->rchlid == NULL)//叶子结点的左右标记置为-1{p1->ltag = p1->rtag = -1;}else//不是叶子结点,就左0右1标记{if (p1->lchild != NULL){p1->ltag = 0;}if (p1->rchlid != NULL){p1->rtag = 1;}coding(p1->lchild);coding(p1->rchlid);}return root;
}
hfmnode* find(hfmnode *root, char s)//查找s结点的位置,因为后面编码的过程是从底往上的
{                               //就是二叉树的查找结点操作;hfmnode *p, *p1;p = root;if (root == NULL)return NULL;else if (root->leaf == s)return root;else{p1 = find(root->lchild, s);if (p1)return p1;elsereturn find(root->rchlid, s);}}
void code(hfmnode *root, stack *st, char s)//编码---自底向上
{hfmnode *p1, *pre;p1 = find(root, s);//找到要编码字符的位置while (p1->parent != NULL)//从叶子往上找,直到找到跟结点,根节点的双亲结点为空{pre = p1->parent;if (pre->lchild == p1)//如果p1是他双亲的左孩子,就把它的左标记0入栈。{                   //因为编码是自叶子结点到跟结点的顺序,最后的编码就是倒序的,当然 也可以用数组来存,但是用栈的话分高一点。push(st, root->ltag);}else if (pre->rchlid == p1){push(st, root->rtag);}p1 = p1->parent;}
}
void printcode(hfmnode *root, stack *st)//输出编码   //A--065    B--090  " "--032
{char a[MAX];int n = 0;int i = 0;printf("请输入您想要编码的字符:");gets_s(a);while (a[i] != '\0'){n = n + 1;i++;}n = n - 1;//i = 0;while (n >= 0){code(root, st, a[n]);//把上面倒序的编码再倒序输出,它就变成正序了。n--;}
}
void decoding(hfmnode *root, char *str)//译码
{char a[MAX];printf("请输入您需要译码的字符串:");scanf_s("%s", a, MAX);int i = 0;//i用于对记录下一个翻译编码的位置int j = 0;//j用于下一个保存字符的位置hfmnode *p1;p1 = root;while (a[i] != '\0') //从根结点向下译码{while (p1->lchild)//翻译出一个字符{if (a[i] == '0') //0向左孩子{p1 = p1->lchild;}else if (a[i] == '1') //1向右孩子{p1 = p1->rchlid;}elsebreak;  //不符合条件就跳出循环i++;}if (p1->leaf == '#'){str[j++] = '#';//编码没有对应译码}elsestr[j++] = p1->leaf; //编码有对应译码p1 = root;//重新从根向下找}str[j] = '\0';//设置结束标志
}hfmnode A[MAX];
hfmnode B[MAX];
int mo;
int u = 0;//算坐标
void getid(hfmnode *root)//把哈夫曼树的每个结点对应到一棵完全二叉树中,给每个结点一个id值。
{                        //即根节点的id为1,他的左孩子的id为他的二倍,他的右孩子为他id的二倍加1//用递归来实现if (root->lchild != NULL){root->lchild->id = 2 * root->id;//他的左孩子的id为他双亲id的二倍root->rchlid->id = 2 * root->id + 1;//他的右孩子的id为他双亲id的二倍加1getid(root->lchild);//往左getid(root->rchlid);//往右}
}
void getleafid(hfmnode *root)//改变叶子的id值,用一个公式,公式在文档中
{if (root->lchild == NULL){root->id = root->id / pow(2, int(log(root->id) / log(2)));//根据公式改idA[mo++] = *root;//把叶子结点放到A数组中root->tag = 1;//给叶子标记为1}else{getleafid(root->lchild);//用递归来实现getleafid(root->rchlid);}
}
void getleaf_x(hfmnode *root)//根据叶子的id值算出叶子的横坐标值,
{                            //然后根据叶子的横坐标值往上算其他结点的横坐标值int n = mo;               //根据叶子的id值在所有叶子中的排序,给他一个横坐标值int i, j;int k;for (i = 0; i < n; i++){k = 1;for (j = 0; j < n; j++){if (A[j].id < A[i].id)k = k + 1;}A[i].p.x = 50 + k * 40;//给每个叶子一个横坐标值,先给到刚才放叶子的数组中,后面再赋值给树中的叶子结点//因为直接给到树中的叶子比较麻烦,所有上面才用到一个存叶子结点的辅助数组A;hfmnode *p1 = find(root, A[i].leaf);//找到叶子的位置,把上面在数组中得到的横坐标值赋值给树中叶子的p.xp1->p.x = A[i].p.x;}
}
void getnode_x(hfmnode *root)//根据孩子结点的横坐标值算双亲结点的横坐标,
{                           //双亲结点的横坐标值等于他子女横坐标值相加除以2,----数学中两点中点的坐标公式if (root)             //通过后序来实现,因为后序是先子女后根{getnode_x(root->lchild);getnode_x(root->rchlid);if (root->tag == 1)//是叶子结点就返回,因为叶子结点的横坐标已经算出来了return;else{root->p.x = (root->lchild->p.x + root->rchlid->p.x) / 2;//中点坐标公式root->tag = 1;//把每个算出来坐标的非叶子结点结点标记为1}}
}
void getnode_y(hfmnode *root)//根据每个结点的id值算纵坐标,叶子单独算,因为叶子的id值已经改变
{hfmnode *p1 = root;if (p1){if (p1->lchild == NULL)//叶子纵坐标单独算,他的纵坐标是在他双亲纵坐标上偏移一个值p1->p.y = p1->parent->p.y + 60;else{p1->p.y = 60 * int((log(p1->id) / log(2)) + 1);//非叶子结点的纵坐标值用他的层数乘以每层的高度。getnode_y(p1->lchild);                         //(log(p1->id) / log(2)) + 1就是每个结点的层数getnode_y(p1->rchlid);                           //到此,每个结点的横坐标和纵坐标的值都已经算出来了,//下面用递归或非递归后序来画出每个结点}}
}
//加点音乐
void jpmusic()//出点音乐
{mciSendString(L"open 出点.mp3 alias jpmusic", 0, 0, 0);mciSendString(L"play jpmusic", 0, 0, 0);
}
//后序递归画树
void postorder_d(hfmnode *root)//递归遍历树
{hfmnode *p1 = root;if (p1){postorder_d(p1->lchild);postorder_d(p1->rchlid);setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 3);setlinecolor(RGB(0, 255, 255));circle(p1->p.x, p1->p.y, 12);setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 1);settextcolor(RGB(255, 182, 193));char s = p1->leaf;if (s != '#'){setbkmode(TRANSPARENT);outtextxy(p1->p.x - 5, p1->p.y - 7, s);}mciSendString(L"close jpmusic", 0, 0, 0);jpmusic();if (p1->lchild){line(p1->p.x, p1->p.y + 12, p1->lchild->p.x, p1->lchild->p.y - 12);settextcolor(RGB(0, 255, 255));char s1 = '0';outtextxy((p1->p.x + p1->lchild->p.x) / 2 - 15, (p1->p.y + p1->lchild->p.y) / 2 - 14, s1);line(p1->p.x, p1->p.y + 12, p1->rchlid->p.x, p1->rchlid->p.y - 12);settextcolor(RGB(0, 255, 255));char s2 = '1';outtextxy((p1->p.x + p1->rchlid->p.x) / 2 + 12, (p1->p.y + p1->rchlid->p.y) / 2 - 12, s2);setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 1);}Sleep(150);}
}
//后序非递归画树
void postorder_f(hfmnode *root)
{stack S;S.top = 0;while (root || S.top != 0){if (root)//但root不指向空时,把他入栈,并将这个结点的tag标记为0,表示他没有被访问过{S.B[S.top] = *root;//入栈S.tag[S.top] = 0;S.top++;root = root->lchild;//先往左走,直到叶子结点}else//到叶子结点{if (S.tag[S.top - 1] == 1)//已经被访问过但还没有出栈,这次就出栈{S.top--;root = &S.B[S.top];//让根节点在指向这个点setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 3);setlinecolor(GREEN);setfillcolor(RED);circle(root->p.x, root->p.y, 12);setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 1);settextcolor(RED);char s = root->leaf;if (s != '#'){setbkmode(TRANSPARENT);outtextxy(root->p.x - 5, root->p.y - 7, s);}mciSendString(L"close jpmusic", 0, 0, 0);jpmusic();Sleep(150);if (root->lchild){line(root->p.x, root->p.y + 12, root->lchild->p.x, root->lchild->p.y - 12);settextcolor(RGB(0, 255, 255));char s1 = '0';outtextxy((root->p.x + root->lchild->p.x) / 2 - 15, (root->p.y + root->lchild->p.y) / 2 - 14, s1);line(root->p.x, root->p.y + 12, root->rchlid->p.x, root->rchlid->p.y - 12);char s2 = '1';outtextxy((root->p.x + root->rchlid->p.x) / 2 + 13, (root->p.y + root->rchlid->p.y) / 2 - 14, s2);setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 1);}root = NULL;}else//没有被访问过{root = &S.B[S.top - 1];S.tag[S.top - 1] = 1;root = root->rchlid;}}}
}
//画树背景
void bk_ground()
{loadimage(NULL, _T("画树背景3.jpg"), width, height);
}
//编码背景
void codedraw(int r)
{initgraph(640, 480, SHOWCONSOLE);//打开控制台if (r == 1)winsD();else if (r == 2)winsS();setbkmode(TRANSPARENT);loadimage(NULL, _T("编码背景.jpg"), 640, 480);}
//译码背景
void decodedraw(int r)
{initgraph(640, 480, SHOWCONSOLE);if (r == 1)winsD();else if (r == 2)winsS();setbkmode(TRANSPARENT);loadimage(NULL, _T("译码背景.jpg"), 640, 480);}
//画树
void drawhfm(hfmnode *root, int r)
{initgraph(width, height);if (r == 1)winsD();else if (r == 2)winsS();setbkcolor(WHITE);//背景色cleardevice();bk_ground();setbkmode(TRANSPARENT);//消除字体白框settextcolor(WHITE);//字体颜色TCHAR h[] = _T("正在准备哈夫曼树的动态可视化,是否开始?");setlinecolor(BLUE);//线的颜色setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 3);//线的粗细line(33, 20, 370, 20);line(33, 20, 33, 55);line(33, 55, 370, 55);line(370, 20, 370, 55);outtextxy(40, 30, h);getchar();setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 1);setlinecolor(GREEN);postorder_d(root);
}

main函数

#include"头文件.h"
#include<stdio.h>
#include<windows.h>
#include<graphics.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<mmsystem.h>
#pragma comment(lib,"Winmm.lib")
#define MAX 1000
MOUSEMSG m;
int W = 640, H = 480;
int see;
//主菜单
TCHAR Pi[] = _T("Project induction");
TCHAR Dp[] = _T("Display huffman");
TCHAR Cd[] = _T("Code");
TCHAR Ef[] = _T("Exit interface");
TCHAR Tp[] = _T("HUFFMAN CODER DECODER");
TCHAR tp[] = _T("Huffman Coder Decoder");
TCHAR tp1[] = _T("动静态存储选择界面");
TCHAR tp_Pi[] = _T("程序简介");
TCHAR Ts[] = _T("Translation");
//介绍
TCHAR name[] = _T("组员成员:xxx xxx xxx xxx");
TCHAR fun[] = _T("程序功能:实现huffman编码、译码、动态显示huffman树");
TCHAR message[] = _T("版本信息:2019.12.25 正式版");
TCHAR exi[] = _T("Exit");
//动静态选择
TCHAR Tip[] = _T("STATIC DYNAMIC SELECTION");
TCHAR dyn[] = _T("动态存储结构");//动态
TCHAR sta[] = _T("静态存储结构");//静态//背景
void background()
{void tip1();loadimage(NULL, _T("显示背景.jpg"), 640, 480);tip1();
}
void background_Pi()
{loadimage(NULL, _T("介绍背景.jpg"), 640, 480);
}
void background_sd()
{void tip2();loadimage(NULL, _T("动静选择背景.jpg"), 640, 480);tip2();
}
//主界面标题(tip1)和动静态选择界面标题(tip2)
void tip1()
{settextcolor(RGB(0, 255, 0));settextstyle(40, 0, _T("方正舒体"));outtextxy(W / 2 - 210, H / 2 - 150, Tp);
}
void tip2()
{settextcolor(RGB(0, 255, 0));settextstyle(40, 0, _T("方正舒体"));outtextxy(W / 2 - 210, H / 2 - 150, Tip);
}
//主界面选项打印
void print_Dp()//display
{setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 130, H / 2 - 75, W / 2 + 130, H / 2 - 45);outtextxy(W / 2 - 110, H / 2 - 75, Dp);
}
void print_Ts()//translation
{setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 130, H / 2 - 35, W / 2 + 130, H / 2 - 5);outtextxy(W / 2 - 80, H / 2 - 35, Ts);
}
void print_Cd()//code
{setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 130, H / 2 + 5, W / 2 + 130, H / 2 + 35);outtextxy(W / 2 - 30, H / 2 + 5, Cd);
}
void print_Pi()//project introduce
{setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 130, H / 2 + 45, W / 2 + 130, H / 2 + 75);outtextxy(W / 2 - 125, H / 2 + 45, Pi);
}
void print_Ef()//exit
{setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 130, H / 2 + 85, W / 2 + 130, H / 2 + 115);outtextxy(W / 2 - 105, H / 2 + 85, Ef);
}
//动静态界面选项打印
void print_dyn()
{settextstyle(30, 15, _T("方正舒体"));setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));rectangle(W / 2 - 100, H / 2 - 55, W / 2 + 100, H / 2 - 25);outtextxy(W / 2 - 90, H / 2 - 55, dyn);
}
void print_sta()
{settextstyle(30, 15, _T("方正舒体"));setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));rectangle(W / 2 - 100, H / 2 - 15, W / 2 + 100, H / 2 + 15);outtextxy(W / 2 - 90, H / 2 - 15, sta);
}
void print_exi()
{settextstyle(30, 15, _T("方正舒体"));setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));rectangle(W / 2 - 100, H / 2 + 25, W / 2 + 100, H / 2 + 55);outtextxy(W / 2 - 25, H / 2 + 25, exi);
}
//主界面和动静态选择界面
void meau()//主界面
{void wins();wins();BeginBatchDraw();background();print_Dp();print_Ts();print_Cd();print_Pi();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void meau_st_dy()//动静态选择界面
{void wins2();wins2();BeginBatchDraw();background_sd();print_dyn();print_sta();print_exi();EndBatchDraw();FlushMouseMsgBuffer();
}
//主界面按动效果
void an_Dp()//display
{BeginBatchDraw();background();setlinecolor(GREEN);settextstyle(25, 15, _T("方正舒体"));settextcolor(RED);rectangle(W / 2 - 130, H / 2 - 75, W / 2 + 130, H / 2 - 45);outtextxy(W / 2 - 108, H / 2 - 72, Dp);print_Ts();print_Cd();print_Pi();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void an_Ts()//translation
{BeginBatchDraw();background();setlinecolor(GREEN);settextstyle(25, 15, _T("方正舒体"));settextcolor(RED);rectangle(W / 2 - 130, H / 2 - 35, W / 2 + 130, H / 2 - 5);outtextxy(W / 2 - 78, H / 2 - 32, Ts);print_Dp();print_Cd();print_Pi();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void an_Cd()//code
{BeginBatchDraw();background();setlinecolor(GREEN);settextstyle(25, 15, _T("方正舒体"));settextcolor(RED);rectangle(W / 2 - 130, H / 2 + 5, W / 2 + 130, H / 2 + 35);outtextxy(W / 2 - 28, H / 2 + 8, Cd);print_Ts();print_Dp();print_Pi();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void an_Pi()//project introduce
{BeginBatchDraw();background();setlinecolor(GREEN);settextstyle(25, 15, _T("方正舒体"));settextcolor(RED);rectangle(W / 2 - 130, H / 2 + 45, W / 2 + 130, H / 2 + 75);outtextxy(W / 2 - 123, H / 2 + 48, Pi);print_Ts();print_Dp();print_Cd();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void an_Ef()//exit interface
{BeginBatchDraw();background();setlinecolor(GREEN);settextstyle(25, 15, _T("方正舒体"));settextcolor(RED);rectangle(W / 2 - 130, H / 2 + 85, W / 2 + 130, H / 2 + 115);outtextxy(W / 2 - 108, H / 2 + 88, Ef);print_Ts();print_Dp();print_Cd();print_Pi();EndBatchDraw();FlushMouseMsgBuffer();
}
//动静态选择界面按动效果
void an_dyn()
{BeginBatchDraw();background_sd();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(255, 0, 255));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 100, H / 2 - 55, W / 2 + 100, H / 2 - 25);outtextxy(W / 2 - 88, H / 2 - 52, dyn);print_sta();print_exi();EndBatchDraw();FlushMouseMsgBuffer();
}
void an_sta()
{BeginBatchDraw();background_sd();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(255, 0, 255));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 100, H / 2 - 15, W / 2 + 100, H / 2 + 15);outtextxy(W / 2 - 88, H / 2 - 12, sta);print_dyn();print_exi();EndBatchDraw();FlushMouseMsgBuffer();
}
void an_exi()
{BeginBatchDraw();background_sd();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(255, 0, 255));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 100, H / 2 + 25, W / 2 + 100, H / 2 + 55);outtextxy(W / 2 - 23, H / 2 + 28, exi);print_dyn();print_sta();EndBatchDraw();FlushMouseMsgBuffer();
}
//主界面选项变色
void Dp_change_colour()//display
{BeginBatchDraw();background();settextstyle(30, 15, _T("方正舒体"));settextcolor(RED);setlinecolor(GREEN);rectangle(W / 2 - 130, H / 2 - 75, W / 2 + 130, H / 2 - 45);outtextxy(W / 2 - 110, H / 2 - 75, Dp);print_Ts();print_Cd();print_Pi();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void Ts_change_colour()//translation
{BeginBatchDraw();background();settextstyle(30, 15, _T("方正舒体"));settextcolor(RED);setlinecolor(GREEN);rectangle(W / 2 - 130, H / 2 - 35, W / 2 + 130, H / 2 - 5);outtextxy(W / 2 - 80, H / 2 - 35, Ts);print_Dp();print_Cd();print_Pi();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void Cd_change_colour()//code
{BeginBatchDraw();settextstyle(30, 15, _T("方正舒体"));settextcolor(RED);setlinecolor(GREEN);rectangle(W / 2 - 130, H / 2 + 5, W / 2 + 130, H / 2 + 35);outtextxy(W / 2 - 30, H / 2 + 5, Cd);print_Dp();print_Ts();print_Pi();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void Pi_change_colour()//project introduce
{BeginBatchDraw();settextstyle(30, 15, _T("方正舒体"));settextcolor(RED);setlinecolor(GREEN);rectangle(W / 2 - 130, H / 2 + 45, W / 2 + 130, H / 2 + 75);outtextxy(W / 2 - 125, H / 2 + 45, Pi);print_Dp();print_Ts();print_Cd();print_Ef();EndBatchDraw();FlushMouseMsgBuffer();
}
void Ef_change_colour()//exit interface
{BeginBatchDraw();settextstyle(30, 15, _T("方正舒体"));settextcolor(RED);setlinecolor(GREEN);rectangle(W / 2 - 130, H / 2 + 85, W / 2 + 130, H / 2 + 115);outtextxy(W / 2 - 105, H / 2 + 85, Ef);print_Dp();print_Ts();print_Cd();print_Pi();EndBatchDraw();FlushMouseMsgBuffer();
}
//动静态界面选项变色
void dyn_change_colour()
{BeginBatchDraw();background_sd();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(255, 0, 255));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 100, H / 2 - 55, W / 2 + 100, H / 2 - 25);outtextxy(W / 2 - 90, H / 2 - 55, dyn);                       //动态print_sta();print_exi();EndBatchDraw();FlushMouseMsgBuffer();
}
void sta_change_colour()
{BeginBatchDraw();background_sd();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(255, 0, 255));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 100, H / 2 - 15, W / 2 + 100, H / 2 + 15);outtextxy(W / 2 - 90, H / 2 - 15, sta);print_dyn();print_exi();EndBatchDraw();FlushMouseMsgBuffer();
}
void exi_change_colour()
{BeginBatchDraw();background_sd();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(255, 0, 255));settextstyle(30, 15, _T("方正舒体"));rectangle(W / 2 - 100, H / 2 + 25, W / 2 + 100, H / 2 + 55);outtextxy(W / 2 - 25, H / 2 + 25, exi);print_dyn();print_sta();EndBatchDraw();FlushMouseMsgBuffer();
}
//按钮音效
void music()
{mciSendString(_T("close mvmusic"), NULL, 0, NULL);mciSendString(_T("open 点击音效.mp3 alias mvmusic"), NULL, 0, NULL);mciSendString(_T("play mvmusic "), NULL, 0, NULL);
}
//说明
void Pro_id()
{void wins_Pi();wins_Pi();cleardevice();settextcolor(RGB(0, 255, 0));setlinecolor(RGB(0, 255, 0));settextstyle(25, 12, _T("方正舒体"));background_Pi();rectangle(W / 2 - 100, (H / 4) * 3, W / 2 + 100, (H / 4) * 3 + 25);outtextxy(W / 2 - 300, H / 4, name);outtextxy(W / 2 - 300, H / 4 - 100, message);outtextxy(W / 2 - 300, H / 2, fun);outtextxy(W / 2 - 25, (H / 4) * 3, exi);while (1){FlushMouseMsgBuffer();m = GetMouseMsg();if (m.uMsg == WM_MOUSEMOVE){if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y >(H / 4) * 3 && m.y < (H / 4) * 3 + 25){setbkmode(TRANSPARENT);setlinecolor(RGB(255, 0, 255));settextcolor(RGB(0, 255, 0));settextstyle(25, 12, _T("方正舒体"));rectangle(W / 2 - 100, (H / 4) * 3, W / 2 + 100, (H / 4) * 3 + 25);outtextxy(W / 2 - 25, (H / 4) * 3, exi);FlushMouseMsgBuffer();}else{BeginBatchDraw();background_Pi();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(0, 255, 0));settextstyle(25, 12, _T("方正舒体"));outtextxy(W / 2 - 300, H / 4, name);outtextxy(W / 2 - 300, H / 4 - 100, message);outtextxy(W / 2 - 300, H / 2, fun);rectangle(W / 2 - 100, (H / 4) * 3, W / 2 + 100, (H / 4) * 3 + 25);outtextxy(W / 2 - 25, (H / 4) * 3, exi);EndBatchDraw();FlushMouseMsgBuffer();}}if (m.uMsg == WM_LBUTTONDOWN && m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y >(H / 4) * 3 && m.y < (H / 4) * 3 + 25){cleardevice();BeginBatchDraw();background_Pi();setlinecolor(RGB(0, 255, 0));settextcolor(RGB(0, 255, 0));settextstyle(25, 12, _T("方正舒体"));outtextxy(W / 2 - 300, H / 4, name);outtextxy(W / 2 - 300, H / 4 - 100, message);outtextxy(W / 2 - 300, H / 2, fun);settextcolor(RGB(255, 0, 255));rectangle(W / 2 - 100, (H / 4) * 3, W / 2 + 100, (H / 4) * 3 + 25);outtextxy(W / 2 - 27, (H / 4) * 3 + 3, exi);EndBatchDraw();}if (m.uMsg == WM_LBUTTONUP && m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y >(H / 4) * 3 && m.y < (H / 4) * 3 + 25){cleardevice();BeginBatchDraw();background();meau();EndBatchDraw();music();FlushMouseMsgBuffer();break;}}
}
void Exi()
{cleardevice();background();getchar();exit(0);
}
//静动态选择
void st_dy_sele(hfmnode *root, hfmnode *root1, stack *st, int see)
//see判断是由画树、编码、译码哪一个进入此函数来执行对应操作
{cleardevice();meau_st_dy();//动静态选择界面int i = 1;//退出此界面的判断条件while (i == 1){i = 1;int r = 0;FlushMouseMsgBuffer();m = GetMouseMsg();switch (m.uMsg){case WM_MOUSEMOVE://鼠标移动信息及文字变色操作{if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 - 55 && m.y < H / 2 - 25){dyn_change_colour();}else if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 - 15 && m.y < H / 2 + 15){sta_change_colour();}else if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 + 25 && m.y < H / 2 + 55){exi_change_colour();}else{meau_st_dy();FlushMouseMsgBuffer();}}break;case WM_LBUTTONDOWN://鼠标左键按下信息及按动效果操作{if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 - 55 && m.y < H / 2 - 25){an_dyn();}else if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 - 15 && m.y < H / 2 + 15){an_sta();}else if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 + 25 && m.y < H / 2 + 55){an_exi();}}break;case  WM_LBUTTONUP://鼠标左键弹起信息及进入对应功能的选择{//动态if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 - 55 && m.y < H / 2 - 25){dyn_change_colour();music();r = 1;if (see == 1)//调用画树界面{drawhfm(root, r);//画出哈夫曼树getchar();closegraph();initgraph(W, H);setbkmode(TRANSPARENT);st_dy_sele(root, root1, st, see);i = 0;}if (see == 2)//调用译码界面{char ste[MAX];decodedraw(r);settextcolor(BLACK);TCHAR op[] = _T("---欢迎来到译码世界---");outtextxy(200, 70, op);decoding(root, ste);int q = 0;int z = 0;int x = 120, y = 120;while (ste[q] != '\0'){char s2;s2 = ste[q];setbkmode(TRANSPARENT);settextcolor(BLACK);if ((x + z * 10) <540){outtextxy(x + z * 10, y, s2);}else{z = 0;y = y + 14;outtextxy(x + z * 10, y, s2);}outtextxy(x + z * 10, y, s2);z++;q++;}getchar();getchar();closegraph();initgraph(W, H);setbkmode(TRANSPARENT);st_dy_sele(root, root1, st, see);i = 0;}if (see == 3)//调用编码界面{codedraw(r);settextcolor(BLACK);TCHAR op[] = _T("!!!欢迎来到01密码世界!!!");outtextxy(200, 70, op);printcode(root, st);int z = 0;int n = st->top;char s;int x = 100, y = 100;while (z<n&&st->top != 0){s = '0' + st->A[st->top - 1];st->top--;settextcolor(BLACK);if ((x + z * 8) <540){outtextxy(x + z * 8, y, s);}else{z = 0;y = y + 14;outtextxy(x + z * 8, y, s);}outtextxy(x + z * 8, y, s);z++;}getchar();closegraph();initgraph(W, H);setbkmode(TRANSPARENT);st_dy_sele(root, root1, st, see);i = 0;}}else if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 - 15 && m.y < H / 2 + 15){sta_change_colour();music();r = 2;if (see == 1){drawhfm(root1, r);//画出哈夫曼树getchar();closegraph();initgraph(W, H);setbkmode(TRANSPARENT);st_dy_sele(root, root1, st, see);i = 0;}if (see == 2){char ste1[MAX];decodedraw(r);settextcolor(BLACK);TCHAR op[] = _T("!!!欢迎来到译码世界!!!");outtextxy(200, 70, op);decoding(root1, ste1);int q = 0;int z = 0;int x = 120, y = 120;while (ste1[q] != '\0'){char s2;s2 = ste1[q];setbkmode(TRANSPARENT);settextcolor(BLACK);if ((x + z * 10) <540){outtextxy(x + z * 10, y, s2);}else{z = 0;y = y + 14;outtextxy(x + z * 10, y, s2);}outtextxy(x + z * 10, y, s2);z++;q++;}getchar();getchar();closegraph();initgraph(W, H);setbkmode(TRANSPARENT);st_dy_sele(root, root1, st, see);i = 0;}if (see == 3){codedraw(r);settextcolor(BLACK);TCHAR op[] = _T("!!!欢迎来到01密码世界!!!");outtextxy(200, 70, op);printcode(root1, st);int z = 0;int n = st->top;char s;int x = 100, y = 100;while (z<n&&st->top != 0){s = '0' + st->A[st->top - 1];//强制类型转换,转换为字符型st->top--;settextcolor(BLACK);if ((x + z * 8) <540)//输出太多就换行{outtextxy(x + z * 8, y, s);}else{z = 0;y = y + 14;outtextxy(x + z * 8, y, s);}outtextxy(x + z * 8, y, s);z++;}getchar();closegraph();initgraph(W, H);setbkmode(TRANSPARENT);st_dy_sele(root, root1, st, see);FlushMouseMsgBuffer();i = 0;}}else if (m.x > W / 2 - 100 && m.x < W / 2 + 100 && m.y > H / 2 + 25 && m.y < H / 2 + 55){exi_change_colour();music();i = 0;}}break;}}cleardevice();meau();FlushMouseMsgBuffer();//清除之前的鼠标消息}
//操作
void mouse_control(hfmnode *root, hfmnode *root1, stack *st, int see)
{cleardevice();meau();while (true){FlushMouseMsgBuffer();//清除之前的鼠标消息m = GetMouseMsg(); // 获取一条鼠标消息switch (m.uMsg){//鼠标移动信息see = 0;case WM_MOUSEMOVE:{if (m.x > (W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 - 75) && m.y < (H / 2 - 45)){Dp_change_colour();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 - 35) && m.y < (H / 2 - 5)){Ts_change_colour();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 5) && m.y < (H / 2 + 35)){Cd_change_colour();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 45) && m.y < (H / 2 + 75)){Pi_change_colour();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 85) && m.y < (H / 2 + 115)){Ef_change_colour();}else{meau();}}break;//左键按下case WM_LBUTTONDOWN:{if (m.x > (W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 - 75) && m.y < (H / 2 - 45)){an_Dp();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 - 35) && m.y < (H / 2 - 5)){an_Ts();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 5) && m.y < (H / 2 + 35)){an_Cd();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 45) && m.y < (H / 2 + 75)){an_Pi();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 85) && m.y < (H / 2 + 115)){an_Ef();}else{meau();}}break;//左键弹起case WM_LBUTTONUP:{if (m.x > (W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 - 75) && m.y < (H / 2 - 45)){Dp_change_colour();music();see = 1;st_dy_sele(root, root1, st, see);}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 - 35) && m.y < (H / 2 - 5)){Ts_change_colour();music();see = 2;st_dy_sele(root, root1, st, see);}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 5) && m.y < (H / 2 + 35)){Cd_change_colour();music();see = 3;st_dy_sele(root, root1, st, see);}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 45) && m.y < (H / 2 + 75)){Pi_change_colour();music();Pro_id();}else if (m.x >(W / 2 - 130) && m.x < (W / 2 + 130) && m.y >(H / 2 + 85) && m.y < (H / 2 + 115)){Ef_change_colour();music();Sleep(200);loadimage(NULL, _T("谢幕.jpg"), 640, 480);Sleep(2000);exit(0);}else{meau();}}break;}}
}
//修改窗口名称
void wins()
{// 获得窗口句柄HWND hWnd = GetHWnd();// 使用 API 函数修改窗口名称SetWindowText(hWnd, tp);
}
void wins2()
{// 获得窗口句柄HWND hWnd = GetHWnd();// 使用 API 函数修改窗口名称SetWindowText(hWnd, tp1);
}
void wins_Pi()
{// 获得窗口句柄HWND hWnd = GetHWnd();// 使用 API 函数修改窗口名称SetWindowText(hWnd, tp_Pi);
}
//动态
int main()
{stack A;stack *st;st = &A;init(st);hfmnode *head, *root;mo = 0;head = sort();//对输入的无序序列进行排序root = creathfm(head);//建造哈夫曼树root = coding(root);//生成01编码root->id = 1;getid(root);//得到每个点的id值getleafid(root);//改变叶子结点的id值getleaf_x(root);//根据每个叶子结点的id值算出它的横坐标X.getnode_x(root);//根据叶子结点的横坐标值算出其他结点的横坐标getnode_y(root);//静态mo = 0;hfmnode *root_1, *head_1;hfmnode A1[MAX];sort_1(A1);//对初始序列进行排序head_1 = &A1[0];root_1 = creathfm_1(head_1, A1);//建树root_1 = coding(root_1);//生成01编码root_1->id = 1;getid(root_1);//得到每个点的id值getleafid(root_1);//改变叶子结点的id值getleaf_x(root_1);//根据每个叶子结点的id值算出它的横坐标X.getnode_x(root_1);//根据叶子结点的横坐标值算出其他结点的横坐标getnode_y(root_1);HWND wnd = GetHWnd();if (MessageBox(wnd, _T("欢迎使用本编码器,是否进入?"), _T("使用提示"), MB_YESNO | MB_ICONQUESTION) == IDYES){initgraph(W, H);mciSendString(_T("open 课设背景音乐.mp3 alias bkmusic"), NULL, 0, NULL);mciSendString(_T("play bkmusic repeat"), NULL, 0, NULL);                   //修改窗口名称setbkcolor(BLUE);           //幕布setbkmode(TRANSPARENT);background();meau();mouse_control(root, root_1, st, see);            //操作mciSendString(_T("close bkmusic"), NULL, 0, NULL);}else{exit(1);}closegraph();}

程序

程序部分截图




程序演示

链接:https://pan.baidu.com/s/112AYnQt25h811nmU5wk

QQ:1579804639

哈夫曼编码器“数据结构课程设计”相关推荐

  1. 哈夫曼编译码器——数据结构课程设计

    2.哈夫曼编/译码器 [问题描述] 用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本.但是,这要求在发送端通过一个编码系统对待传数据预先编码,在接收端将传来的数据进行译码(复 ...

  2. 数据结构哈夫曼编码解码课程设计

    数据结构课程设计 源代码在另一篇博客可以找到:https://blog.csdn.net/qq_40513633/article/details/85055411?ops_request_misc=% ...

  3. 哈夫曼编码解码课程设计源代码

    数据结构课程设计 "哈夫曼编码/译码器 设计一个利用哈夫曼算法的编码和译码系统,重复显示并处理以下项目,知道选择退出为止/ [基本要求] 1)将权值数据存放在数据文件(文件名data.txt ...

  4. 数据结构课程设计-哈夫曼编解码器

    哈夫曼编解码器 最近在整理课程资料,就决定将自己完成的数据结构课程设计上传到CSDN里面. (1)问题描述 使用哈夫曼编码,实现文本文件的编码和解码,具体要求如下: ① 文本文件 data.txt 中 ...

  5. 【课程设计|C++】设计一个哈夫曼编码器/译码器设计

    目录 前言 设计一个哈夫曼编码器/译码器设计 [基本功能] [基本要求] 代码 实验结果 结语 前言 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出-   自我介绍 ଘ(੭ˊ ...

  6. 数据结构课程设计[2023-01-19]

    数据结构课程设计[2023-01-19] 数据结构课程设计 一.课程设计要求 实现指定的题目(学号最后两位%4+1),并撰写课程设计报告. 独立完成,功能不完备也没关系,只要是自己做的 使用 C.C ...

  7. 大学数据结构课程设计题目

    数据结构课程设计题目 1.         飞机订票系统(限1 人完成) 任务:通过此系统可以实现如下功能: 录入: 可以录入航班情况(数据可以存储在一个数据文件中,数据结构.具体数据自定) 查询: ...

  8. C/C++数据结构课程设计安排

    C/C++数据结构课程设计安排 数据结构课程设计安排 课程设计学时:32学时 课程设计目的:综合应用数据结构课程中所学的数据结构:线性表.栈.队列.数组.广义表.树.二叉树.图.查找表中的一种或多种数 ...

  9. 数据结构课程设计报告——Huffman编码

    目录 一. 问题描述与要求 二. 需求分析 三. 设计 3.1 设计思想 3.1.1 数据与操作的特性 3.1.2 数据结构设计 3.1.3 算法设计 3.2 设计表示 3.2.1 函数调用关系图 3 ...

最新文章

  1. JSBridge实战
  2. php任务奖励体系,phpwind7.5完备的积分体系
  3. 关于在CVS下无法获取更新的解决方法!!!
  4. 2010年11月8日,早会资料(日本的文化节)。
  5. 【ArcGIS 10.2新特性】地理数据(Geodatabase 和database)10.2 新特性
  6. python数据结构-顺序表
  7. 2020软考信息系统项目管理师-案例分析真题解析视频课程-任铄(小任老师)-专题视频课程...
  8. jquery实现进度条
  9. 谷歌翻译API-python接口-Googletrans
  10. a0图框标题栏尺寸_a0图纸尺寸(a0图纸标题栏尺寸标准国标)
  11. 算法学习---- 随机森林的基本原理
  12. (个人笔记)英语语法之动词时态
  13. 基于法律裁判文书的法律判决预测
  14. 第五次作业 刘惠惠 自动生成的方法存根
  15. wincap网络数据包的捕获
  16. Enovia文件协作服务器安装,Enovia用户操作手册.doc
  17. 地图学的基础知识_天文坐标系_大地坐标系_地心坐标系及其相关概念
  18. vue-devtools 具体使用配置详情
  19. Oracle递归查询的原理
  20. 数据库 | Mysql - [索引]

热门文章

  1. 《编译原理-龙书》练习第4章
  2. 解决mplfinance绘制箱体图一字涨停k线颜色错误的情况
  3. C语言实现总体方差,总体标准差,样本方差,样本标准差
  4. 【网络运维】小平头PingTow网络IP导入检测工具软件开发源代码分享
  5. MBR膜生物反应器,生物反应器原理-世来福
  6. 3dsmax怎么添加uv坐标_3dmax缺少贴图坐标怎么添加uvmap修改器
  7. mysql-8.0.12_MySQL 8.0.12-winx64 安装
  8. 【分布式训练-Ring AllReduce】
  9. matlab 分图 总标题,matlab中figure有多个图时,设定总标题的方法
  10. php动画效果,动画效果总结