本博客将以代码的形式详细讲解二叉树的所有算法,包括创建二叉树,二叉树的三种遍历方式,二叉树的各种属性算法,如:求高度,求叶子节点数,求节点数,以及二叉树最常见的应用哈夫曼树,代码如下:
# include<stdio.h>
# include<string.h>
# include<conio.h>
# include<stdlib.h>
# define N 1
# define M 2*N-1
typedef char * HC[N+1];
typedef struct bt
{
char x;
struct bt *lchild;
struct bt *rchild;
}bt,*pbt;
typedef struct
{
int parent;
int weight, lchild, rchild;
}HT[M+1];
void creatbt(pbt *root)
{
char ch=getchar();
if(ch==' ')
*root=NULL;
else
{
*root=(pbt)malloc(sizeof(bt));
(*root)->x=ch;
creatbt(&((*root)->lchild));
creatbt(&((*root)->rchild));
}
}
void preorder(pbt root)
{
if(root!=NULL)
{
printf("%c",root->x);
preorder(root->lchild);
preorder(root->rchild);
}
}
void inorder(pbt root)
{
if(root!=NULL)
{
inorder(root->lchild);
printf("%c",root->x);
inorder(root->rchild);
}
}
void postorder(pbt root)
{
if(root!=NULL)
{
postorder(root->lchild);
postorder(root->rchild);
printf("%c",root->x);
}
}
int btdepth(pbt root,int h)
{
static int depth=0;
if(root!=NULL)
{
if(h>depth) depth=h;
btdepth(root->lchild,h+1);
btdepth(root->rchild,h+1);
}
return depth;
}
int nodenum(pbt root)
{
static int n=0;
if(root!=NULL)
{
n++;
nodenum(root->lchild);
nodenum(root->rchild);
}
return n;
}
int leafnum(pbt root)
{
static int n=0;
if(root!=NULL)
{
leafnum(root->lchild);
leafnum(root->rchild);
if((root->lchild==NULL)&&(root->rchild==NULL))
n++;
}
return n;
}
void select(HT ht,int n,int *x,int *y)
{
int i,min1=100,min2=200;
for(i=1;i<=n;i++)
{
if(ht[i].parent==0&&ht[i].weight<min1)
{
min1=ht[i].weight;
*x=i;
}
}
for(i=1;i<=n;i++)
{
if(ht[i].parent==0&&ht[i].weight<min2&&i!=*x)
{
min2=ht[i].weight;
*y=i;
}
}
}
void hafuman(HT ht,int w[],int n)
{
int i,k,m,x,y;
for(i=1;i<=n;i++)
{
ht[i].weight=w[i];
ht[i].parent=ht[i].lchild=ht[i].rchild=0;
}
m=2*n-1;
for(i=n+1;i<=m;i++)
{
ht[i].parent=ht[i].lchild=ht[i].rchild=0;
}
for(i=n+1;i<=m;i++)
{
select(ht,i-1,&x,&y);//选择parent为0且权值最小的结点
ht[i].weight=ht[x].weight+ht[y].weight;
ht[i].lchild=x;
ht[i].rchild=y;
ht[x].parent=ht[y].parent=i;
}
}
void hafumancode(HT ht,HC hc,int n)
{
int i,c,p,start;
char * cd=(char *)malloc(n*sizeof(char));
cd[n-1]='\0';//此处的cd用来存储当前哈夫曼码,可供循环利用,相当于一个暂存器
for(i=1;i<=n;i++)//求n个叶子结点的哈夫曼码
{
start=n-1;
c=i;//因为下面会循环更新孩子结点,所以不能用i(否则第一次for循环后i可能就不再是1),可将i的值提前赋给c
p=ht[i].parent;
while(p!=0)//只要p不是根结点
{
start--;//此语句用来循环更新存储下标
if(ht[p].lchild==c)
//cd[i]='0';//错误。注意应从叶子结点开始向上推
cd[start]='0';
else
//cd[i]='1';
cd[start]='1';
c=p;//此语句用来循环更新孩子结点
p=ht[p].parent;
}
hc[i]=(char *)malloc((n-start)*sizeof(char));
strcpy(hc[i],&cd[start]);
}
free(cd);
for(i=1;i<=n;i++)
{
//printf("%d的哈夫曼码为%s\n",ht[i],hc[i]);错误,ht[i]为结构数组,应写其成员
printf("%d的哈夫曼码为%s\n",ht[i].weight,hc[i]);
}
}
void main()
{
int n;
pbt root;
printf("\t\t-----------------------------------------\n");
printf("\t\t1.创建二叉树 2.遍历二叉树\n");
printf("\t\t3.二叉树的属性 4.哈夫曼树\n");
printf("\t\t5.退出\n");
printf("\t\t-----------------------------------------\n");
while(1)
{
printf("请选择功能模块(1-5)\n");
scanf("%d",&n);
//char ch=getche();
getchar();
switch(n)
{
case 1:{printf("请以先序扩展创建二叉树(空结点用空格代替)\n");creatbt(&root);}break;
case 2:
{
printf("遍历二叉树\n");
printf("\t\t[1]先序遍历\n");
printf("\t\t[2]中序遍历\n");
printf("\t\t[3]后序遍历\n");
printf("\t\t[4]返回主菜单\n");
while(1)
{
int n;
printf("请选择(1-4):\n");
scanf("%d",&n);
if(1==n)
preorder(root);
if(2==n)
inorder(root);
if(3==n)
postorder(root);
//else//不能这样写,因为这个else只能与上一个if配对,所以当n!=3时break都会执行
if(n==4)
break;
}
}break;
case 3:
{
int h=1,n;
printf("二叉树属性\n");
printf("\t\t[1]二叉树高度\n");
printf("\t\t[2]二叉树结点数\n");
printf("\t\t[3]二叉树叶子结点\n");
printf("\t\t[4]返回主菜单\n");
while(1)
{
printf("请选择:(1-4)\n");
scanf("%d",&n);
if(1==n)
printf("该二叉树的高度为%d\n",btdepth(root,h));
if(2==n)
printf("该二叉树的结点数为%d\n",nodenum(root));
if(3==n)
printf("该二叉树的叶子结点数为%d\n",leafnum(root));
if(n==4)
break;
}
}break;
case 4:
{
HT ht;
HC hc;
int n,i;
printf("请输入叶子结点的个数\n");
scanf("%d",&n);
int * w=(int *)malloc((n+1)*sizeof(int));//此语句必须位于scanf的下面
for(i=1;i<=n;i++)
{
printf("请输入第%d个叶子结点的权值\n",i);
scanf("%d",&w[i]);
}
hafuman(ht,w,n);hafumancode(ht,hc,n);
}break;
case 5:exit(1);
}
}
}
程序运行结果如下:
注意对于同一个二叉树,哈夫曼码的结果不唯一,上述输出只是一种情况。