本文将复习二叉树的相关操作及应用
一、二叉树的节点定义
typedef struct node
{
int data;
struct node *lchild;
struct node *rchild;
}node,*tree;
我们使用二叉链表来存储树,比较好理解
二、遍历及其应用
1.递归遍历算法
最常用的就是递归遍历,这个算法非常简单,也非常容易理解,代码也比较简洁,其内容在书上163页
2.非递归遍历算法(170-174页)
基于栈我们可以不用递归来实现二叉树的遍历,下面是代码
typedef struct
{
tree arr[50];
int top;
}stack;//栈的定义
void init(stack *s)//初始化
{
s->top=-1;
}
void push(stack *s,tree ch)//进栈
{
s->top++;
s->arr[s->top]=ch;
}
tree pop(stack *s)//出栈
{
tree c=s->arr[s->top];
s->top--;
return c;
}
//上面的基本上是栈相关操作,为了大家理解把它写了出来,下面是正文
void pre(tree root)//先序遍历
{
stack s;
init(&s);
node *p;
p=root;
while(p||(s.top!=-1))
{
if(p)
{
printf("%c ",p->data);//遍历根
push(&s,p);
p=p->lchild;//左子树
}
else
{
p=pop(&s);
p=p->rchild;//右子树
}
}
}
void mid(tree root)//中序
{
stack s;
init(&s);
tree p=root;
while(p||(s.top!=-1))
{
if(p)
{
push(&s,p);
p=p->lchild;//左子树
}
else
{
p=pop(&s);
printf("%c ",p->data);//遍历根
p=p->rchild;//右子树
}
}
}
void post(tree root)//后序遍历,相较前两个较为复杂一点
{
node *p,*q;
stack s;
q=NULL;
p=root;
init(&s);
while(p||(s.top!=-1))
{
if(p)//左子树
{
push(&s,p);
p=p->lchild;
}
else
{
p=s.arr[s.top];
if(p->rchild==NULL||p->rchild==q)//没有右孩子或右孩子已经遍历过
{
printf("%c ",p->data);//访问根
q=p;
p=pop(&s);
p=NULL;
}
else//右子树
{
p=p->rchild;
}
}
}
}
3.递归的应用
3.1输出节点:上面的算法其实已经输出了,此处不再赘述
3.2统计叶子节点数目:166页,算法比较简单,此处不赘述
3.3通过遍历去创建二叉链表:167页算法6.7,也比较简单,此处不赘述
3.4求二叉树的高度:168页6.8
int treedepth(tree root)
{
int hl,hr,max;//hl是左子树的高度,hr右子树的高度
if(root)
{
hl=treedepth(root->lchild);//求左子树的高度
hr=treedepth(root->rchild);//求右子树的高度
max=(hl>hr)?hl:hr;//得到大者
return max+1;//返回树的深度,记得+1
}
else
return 0;//空树否则返回0
}
先序递归求二叉树的高度也比较简单,不再赘述
3.5横向树形显示二叉树
169页6.10
本算法使用逆中序的输出方法,结合了上面求高度的算法设计出来的,层深决定左右位置,先右子树,再根,最后左子树。
void printtree(tree root,int n)
{
if(!root)
return;
printtree(root->lchild,n+1);
for(int i=0;i<n;i++)
{
printf(" ");
}
printf("%c\n",root->data);
printtree(root->rchild,n+1);
}
三、哈夫曼树的创建及哈夫曼编码
1.哈夫曼树类型定义及编码,191-196页
#include <iostream>
#include <string.h>
using namespace std;
typedef struct
{
char data;//这个是为了后面哈夫曼编码作为铺垫
int weight;
int parent;
int lchild;
int rchild;
}node,*hftree;
void select(hftree ht, int n, int &s1, int &s2)
{
s1=s2=0; // 初始化为0,表示尚未找到
int min1=32767,min2=32767;
for (int i=1;i<=n;i++)
{
if (ht[i].parent==0)
{
if (ht[i].weight<min1)
{
min2=min1;
s2=s1;
min1=ht[i].weight;
s1=i;
}
else if(ht[i].weight<min2)
{
min2=ht[i].weight;
s2=i;
}
}
}
}
void crthuffmantree(hftree ht,int w[],int n)
{
int i,s1,s2,temp;
for(i=1;i<=n;i++)//叶子节点初始化
{
temp=96+i;
ht[i].data=static_cast<char>(temp);
ht[i].lchild=ht[i].rchild=ht[i].parent=0;
ht[i].weight=w[i];
}
for(i=n+1;i<=2*n-1;i++)//非叶节点初始化
{
ht[i].weight=ht[i].lchild=ht[i].rchild=ht[i].parent=0;
}
for(i=n+1;i<=2*n-1;i++)//生成哈夫曼树
{
select(ht,i-1,s1,s2);//挑选最小的两个节点
ht[i].weight=ht[s1].weight+ht[s2].weight;
ht[s1].parent=ht[s2].parent=i;
ht[i].lchild=s1;
ht[i].rchild=s2;
}
}
void huffmancode(hftree ht,char code[][10],int n)//哈夫曼编码,和书上略微不同,我传的是二维数组
{
char *cd;
cd=(char*)malloc(n*sizeof(char));
cd[n-1]='\0';
int i,start,c,p;
for(i=1;i<=n;i++)
{
start=n-1;
c=i;
p=ht[i].parent;
while(p)
{
start--;
if(ht[p].lchild==c)
cd[start]='0';
else
cd[start]='1';
c=p;
p=ht[p].parent;//找双亲
}
strcpy(code[i],&cd[start]);
}
free(cd);
}
int main()
{
int n,w[10];
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
}
node ht[40];
crthuffmantree(ht,w,n);
char code[10][10];//存放编码的二维字符数组
huffmancode(ht,code,n);
for(int i=1;i<=n;i++)
{
cout<<ht[i].data<<":";
for(int j=0;code[i][j]!='\0';j++)
{
cout<<code[i][j];
}
cout<<endl;
}
return 0;
}
2.编码及解码
//这部分请把它插到上个代码的后面使用
char str1[50],str2[50];
scanf("%10s",str1);//输入字符串输入二进制串
scanf("%100s",str2);//
for(i=0;str1[i]!='\0';i++)
{
int temp=str1[i];//把字符的ASCII码值-96对应到code对应行数
for(j=0;code[temp-96][j]!='\0';j++)//根据哈夫曼编码求二进制串的过程:把每个字符对应的编码直接输出即可
{
cout<<code[temp-96][j];
}
}
i=0;
while(i<strlen(str2))//类似于串的BF匹配算法
{
for(j=1;j<=n;j++)//j控制行
{
int flag=1;
for(k=0;code[j][k]!='\0';k++)//k控制列
{
if(code[j][k]!=str2[i])//如果不等,回溯
{
flag=0;
i-=k;
break;
}
i++;
}
if(flag)//匹配成功,则输出
cout<<ht[j].data;
}
}
本文到此结束,哈夫曼树的难度就有一点大了,程序还是调试了一段时间的,本系列文章目前为止还没有参考网上大佬,仅参考了课本,目的就是为了用书上的思维写,帮助大家理解,下一章我们写图,我个人认为是数据结构最难的一部分了,加油