/*二叉树的基本操作及哈夫曼编码/译码系统的实现
实验目的:
1、掌握二叉树的二叉链表存储表示及遍历操作实现方法。
2、实现二叉树遍历运算的应用:求二叉树中叶子结点个数、结点总数、二叉树的高度、交换
二叉树的左右子树等。
3、掌握二叉树的应用——哈夫曼编码的实现。*/
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>//bool类型
///////////////*完成二叉树的先序创建、先序遍历、中序遍历、后序遍历等*/////////////
#define ElemType int
typedef struct btnode{
int element;
struct btnode *lChild;
struct btnode *rChild;
}BTNode;
typedef struct binarytree{
BTNode *root;
}BinaryTree;
//先序遍历构建二叉树
BTNode* PreCreateBT(BTNode *t){
char ch;
ch=getchar();
if(ch=='#')
t=NULL;
else{
t=(BTNode *)malloc(sizeof(BTNode));
t->element=ch;
t->lChild=PreCreateBT(t->lChild);
t->rChild=PreCreateBT(t->rChild);
}
return t;
}
void PreMakeTree(BinaryTree *bt){
bt->root=PreCreateBT(bt->root);
}
/*另一种建树方法
void Create(BinaryTree *bt){
bt->root=NULL;
}
BTNode* NewNode(ElemType x,BTNode *ln,BTNode *rn){
BTNode *p=(BTNode *)malloc(sizeof(BTNode));
p->element=x;
p->lChild=ln;
p->rChild=rn;
return p;
}
bool Root(BinaryTree *bt,ElemType *x){
if(bt->root){
x=&bt->root->element;
return false;
}
else
return false;
}
void MakeTree(BinaryTree *bt,ElemType e,BinaryTree *left,BinaryTree *right){
if(bt->root||left==right)
return ;
bt->root=NewNode(e,left->root,right->root);
left->root=right->root=NULL;
}
*/
//先序遍历 根-左-右
void PreOrder(BTNode* t){
if(!t)
return;
printf("%c",t->element);//打印根节点
PreOrder(t->lChild);//遍历左子树
PreOrder(t->rChild);//遍历右子树
}
void PreOrderTree(BinaryTree *bt){
PreOrder(bt->root);
printf("\n");
}
//中序遍历 左-根-右
void MidOrder(BTNode* t){
if(!t)
return;
MidOrder(t->lChild);//遍历左子树
printf("%c",t->element);//打印根节点
MidOrder(t->rChild);//遍历右子树
}
void MidOrderTree(BinaryTree *bt){
MidOrder(bt->root);
printf("\n");
}
//后序遍历 左-右-根
void LastOrder(BTNode* t){
if(!t)
return;
LastOrder(t->lChild);//遍历左子树
LastOrder(t->rChild);//遍历右子树
printf("%c",t->element);//打印根节点
}
void LastOrderTree(BinaryTree *bt){
LastOrder(bt->root);
printf("\n");
}
//清空二叉树
void Clear(BTNode* t){
if(!t)
return ;
Clear(t->lChild);
Clear(t->rChild);
free(t);
}
void TreeClear(BinaryTree *bt){
Clear(bt->root);
}
//////*实现求二叉树结点个数、叶子结点个数、二叉树的高度以及交换二叉树所有左右子树的操作。*/////
//求二叉树的结点个数
int TSize(BTNode *t){
if(!t)
return 0;
else
return TSize(t->lChild)+TSize(t->rChild)+1;
}
int TreeSize(BinaryTree *bt){
return TSize(bt->root);
}
//求二叉树的叶子结点个数
int LeafSize(BTNode *t){
if(!t)
return 0;
else{
int x=LeafSize(t->lChild);
int y=LeafSize(t->rChild);
//printf("%d %d\n",x,y);
if(x==0&&y==0)
return 1;
else return x+y;
}
}
int LeafTreeSize(BinaryTree *bt){
return LeafSize(bt->root);
}
int max(int a,int b){
if(a>=b)return a;
else return b;
}
//求二叉树的高度
int Height(BTNode *t){
if(!t)
return 0;
else
return max(Height(t->lChild),Height(t->rChild))+1;
}
int TreeHeight(BinaryTree *bt){
return Height(bt->root);
}
//交换二叉树所有左右子树的操作
void Exchange(BTNode *t){
if(!t)
return;
Exchange(t->lChild);
Exchange(t->rChild);
BTNode *a;
a=t->lChild;
t->lChild=t->rChild;
t->rChild=a;
}
void ExchangeTree(BinaryTree *bt){
Exchange(bt->root);
}
///////////////////*实现哈夫曼树的创建、哈夫曼编码以及解码的实现。*////////////////////
typedef struct hfmTNode{
char element; //结点的数据域
int w; //结点的权值
int code;
struct hfmTNode *lChild; //结点的左孩子指针
struct hfmTNode *rChild; //结点的右孩子指针
}HFMTNode;
//优先权队列的算法实现
typedef struct priorityQueue{
HFMTNode **elements;
int n;
int maxSize;
}PriorityQueue;
//创建一个空的优先权队列
void CreatePQ(PriorityQueue *PQ,int mSize){
PQ->maxSize=mSize;
PQ->n=0;
PQ->elements=(HFMTNode**)malloc(mSize*sizeof(HFMTNode*));
for(int i=0;i<mSize;i++){
PQ->elements[i] = (HFMTNode*)malloc(sizeof(HFMTNode));
PQ->elements[i]->w=0;
}
}
//销毁一个优先权队列,释放其占用的空间
void Destroy(PriorityQueue *PQ){
free(PQ->elements);
PQ->n=0;
PQ->maxSize=0;
}
//判断优先权队列是否为空
int isEmpty(PriorityQueue *PQ){
if(PQ->n==0)
return 1;
else
return 0;
}
//判断优先权队列是已满
int isFull(PriorityQueue *PQ){
if(PQ->n>=PQ->maxSize)
return 1;
else
return 0;
}
//获取当前优先权队列中的元素个数
int Size(PriorityQueue *PQ){
return PQ->n;
}
//向上调整
void AdjustUp(HFMTNode* heap[],int current){
int p=current;
HFMTNode* temp=(HFMTNode*)malloc(sizeof(HFMTNode));
while(p>0){
int parent = (p - 1) / 2;
// printf("%d\n",heap[p]->w);
// printf("%d\n",heap[parent]->w);
if(heap[p]->w<heap[parent]->w){//若p指向的元素小于其双亲结点,则与双亲结点交换
temp=heap[p];
heap[p]=heap[parent];
heap[parent]=temp;
p=parent;//将p向上移动至当前考察元素双亲结点的位置
}
else
break;//若p指向的元素不小于其双亲结点,则调整完毕
}
}
//向下调整
void AdjustDown(HFMTNode *heap[],int current,int border){
int p=current;
int minChild;
HFMTNode *temp=(HFMTNode*)malloc(sizeof(HFMTNode));
while(2*p+1<=border){//p不是叶子节点,执行调整
if((2*p+2)<=border&&heap[2*p+1]->w>heap[2*p+2]->w)
minChild=2*p+2;//右孩子存在且较小,则minChild指向p的右孩子
else
minChild=2*p+1;//左孩子存在且较小,则minChild指向p的左孩子
if(heap[p]->w<=heap[minChild]->w)
break;//若当前结点不大于其最小的孩子,则调整结束
else{//否则将p与其最小孩子交换
temp=heap[p];
heap[p]=heap[minChild];
heap[minChild]=temp;
p=minChild;//设置下轮循环待考察的元素的位置(即当前下移元素的位置)
}
}
}
//在优先权队列中增加一个新元素x
void Append(PriorityQueue *PQ,HFMTNode *x){
if(isFull(PQ))return;
PQ->elements[PQ->n]->element=x->element;
PQ->elements[PQ->n]->w=x->w;
PQ->elements[PQ->n]->lChild=x->lChild;
PQ->elements[PQ->n]->rChild=x->rChild;
// PQ->elements[PQ->n]=x;
PQ->n++;
AdjustUp(PQ->elements,PQ->n-1);
}
//取出优先级最高的元素,利用参数x返回,并在优先权队列中删除该元素
HFMTNode* Serve(PriorityQueue *PQ){
if(isEmpty(PQ))
return NULL;
HFMTNode* p=(HFMTNode*)malloc(sizeof(HFMTNode));
p->w = PQ->elements[0]->w;
p->element = PQ->elements[0]->element;
p->lChild = PQ->elements[0]->lChild;
p->rChild = PQ->elements[0]->rChild;
PQ->n--;
PQ->elements[0]->w=PQ->elements[PQ->n]->w;
PQ->elements[0]->lChild=PQ->elements[PQ->n]->lChild;
PQ->elements[0]->rChild=PQ->elements[PQ->n]->rChild;
PQ->elements[0]->element=PQ->elements[PQ->n]->element;
//PQ->elements[0]=PQ->elements[PQ->n-1];
// PQ->n--;
AdjustDown(PQ->elements,0,PQ->n-1);
return p;
}
HFMTNode* CreateHFMTree(char data[],int w[],int m){
PriorityQueue PQ;//定义优先权队列PQ,用于存放二叉树根结点指针
HFMTNode* x=(HFMTNode*)malloc(sizeof(HFMTNode));
HFMTNode* y=(HFMTNode*)malloc(sizeof(HFMTNode));
HFMTNode* z=(HFMTNode*)malloc(sizeof(HFMTNode));//x,y,z 为二叉树变量
CreatePQ(&PQ,m); //初始化优先权队列 PQ,设优先权值存在根结点数据域
for(int i=0; i<m; i++){
// printf("w[i]=%d data=%d\n",w[i],data[i]);
x->element=data[i];
x->code=0;
x->w=w[i];
x->lChild=NULL;
x->rChild=NULL;
Append(&PQ,x); //将新创建的二叉树插入优先权队列
}
while(PQ.n>1){
x=Serve(&PQ);//从PQ中取出根结点权值最小的二叉树,存入x
// printf("serve=%d PQ-N=%d\n",PQ.elements[0]->w,PQ.n);
y=Serve(&PQ); //从PQ中取出根结点权值次小的二叉树,存入y
//合并x和y,作为新二叉树z的左右子树,z的优先权值等于x和y的优先权值之和
z->w=x->w+y->w;
//printf("x=%dy=%dz=%d\n",x->w,y->w,z->w);
if(x->w < y->w) //设置左子树根结点权值小于右子树
{
z->lChild=x;
z->rChild=y;
}
else{
z->rChild=x;
z->lChild=y;
}
Append(&PQ,z);//将合并生成的新二叉树z插入优先权队列
}
x=Serve(&PQ); //获取优先权队列中唯一的二叉树,存入x,该二叉树即为哈夫曼树
return x;
}
void HFMcode(HFMTNode *h){
// HFMTNode* p=(HFMTNode*)malloc(sizeof(HFMTNode));
// HFMTNode* t=(HFMTNode*)malloc(sizeof(HFMTNode));
// p=h;
if(h->lChild==NULL||h->rChild==NULL){
printf("数据为%c,权值为%d的哈夫曼编码为:",h->element,h->w);
int t=h->code,i=0;
int s[10];
while(t!=0){
s[i]=t%10;
t=t/10;
i++;
}
while(i){
if(s[i-1]==8)
printf("0");
else printf("1");
i--;
}
printf("\n");
return;
}
h->lChild->code=h->code*10+8;
h->rChild->code=h->code*10+1;
HFMcode(h->lChild);
HFMcode(h->rChild);
}
int HFMdecode(HFMTNode *h,char* s){
int i=0;
HFMTNode* x=h;
int a;
while(s[i]!='\0'){
if(s[i]=='0')h=h->lChild;
else if(s[i]=='1')h=h->rChild;
i++;
}
a=h->element;
return a;
}
//先序遍历 根-左-右
void Pre(HFMTNode* t){
if(!t)
return;
t->code=0;
printf("%d ",t->w);//打印根节点
Pre(t->lChild);//遍历左子树
Pre(t->rChild);//遍历右子树
}
int main(){
BinaryTree a;
printf("请输入二叉树:");
PreMakeTree(&a);//先序创建树
/* 建树另一种方法
BinaryTree a,b,x,y,z;
Create(&a);
Create(&b);
Create(&x);
Create(&y);
Create(&z);
MakeTree(&y,'E',&a,&b);
MakeTree(&z,'F',&a,&b);
MakeTree(&x,'C',&y,&z);
MakeTree(&y,'D',&a,&b);
MakeTree(&z,'B',&y,&x);*/
printf("二叉树的先序遍历为:");
PreOrderTree(&a);//先序遍历
printf("二叉树的中序遍历为:");
MidOrderTree(&a);//中序遍历
printf("二叉树的后序遍历为:");
LastOrderTree(&a);//后序遍历
printf("二叉树的结点个数为:%d\n",TreeSize(&a));
printf("二叉树的叶子结点个数为:%d\n",LeafTreeSize(&a));
printf("二叉树的高度为:%d\n",TreeHeight(&a));
ExchangeTree(&a);
printf("二叉树的先序遍历为:");
PreOrderTree(&a);//先序遍历
printf("二叉树的中序遍历为:");
MidOrderTree(&a);//中序遍历
printf("二叉树的后序遍历为:");
LastOrderTree(&a);//后序遍历
TreeClear(&a);//清空树
char data[100];
int w[100];
int m;
//哈夫曼树的创建、哈夫曼编码以及解码的实现d
printf("请输入需要编码的数据个数:");
scanf("%d",&m);
for(int i=0;i<m;i++){
printf("请输入第%d个数据和权值:\n",i+1);
char cc=getchar();
printf("数据为=");
scanf("%c",&data[i]);
printf("权值为=");
scanf("%d",&w[i]);
}
//创建哈夫曼树
HFMTNode *HFM=CreateHFMTree(data,w,m);
//给哈夫曼树进行编码
printf("哈夫曼树的先序遍历输出结果为:");
Pre(HFM);
printf("\n");
HFMcode(HFM);
char s[10];
printf("请输入需要解码的哈夫曼编码:");
scanf("%s",s);
printf("%s所对应的数据为:%c",s,HFMdecode(HFM,s));
return 0;
}