本博客将以代码的形式详细讲解二叉树的所有算法,包括创建二叉树,二叉树的三种遍历方式,二叉树的各种属性算法,如:求高度,求叶子节点数,求节点数,以及二叉树最常见的应用哈夫曼树,代码如下:
# 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]=' ';//此处的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 ",ht[i],hc[i]);错误,ht[i]为结构数组,应写其成员 printf("%d的哈夫曼码为%s ",ht[i].weight,hc[i]); } } void main() { int n; pbt root; printf(" ----------------------------------------- "); printf(" 1.创建二叉树 2.遍历二叉树 "); printf(" 3.二叉树的属性 4.哈夫曼树 "); printf(" 5.退出 "); printf(" ----------------------------------------- "); while(1) { printf("请选择功能模块(1-5) "); scanf("%d",&n); //char ch=getche(); getchar(); switch(n) { case 1:{printf("请以先序扩展创建二叉树(空结点用空格代替) ");creatbt(&root);}break; case 2: { printf("遍历二叉树 "); printf(" [1]先序遍历 "); printf(" [2]中序遍历 "); printf(" [3]后序遍历 "); printf(" [4]返回主菜单 "); while(1) { int n; printf("请选择(1-4): "); 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("二叉树属性 "); printf(" [1]二叉树高度 "); printf(" [2]二叉树结点数 "); printf(" [3]二叉树叶子结点 "); printf(" [4]返回主菜单 "); while(1) { printf("请选择:(1-4) "); scanf("%d",&n); if(1==n) printf("该二叉树的高度为%d ",btdepth(root,h)); if(2==n) printf("该二叉树的结点数为%d ",nodenum(root)); if(3==n) printf("该二叉树的叶子结点数为%d ",leafnum(root)); if(n==4) break; } }break; case 4: { HT ht; HC hc; int n,i; printf("请输入叶子结点的个数 "); scanf("%d",&n); int * w=(int *)malloc((n+1)*sizeof(int));//此语句必须位于scanf的下面 for(i=1;i<=n;i++) { printf("请输入第%d个叶子结点的权值 ",i); scanf("%d",&w[i]); } hafuman(ht,w,n);hafumancode(ht,hc,n); }break; case 5:exit(1); } } }
程序运行结果如下:
注意对于同一个二叉树,哈夫曼码的结果不唯一,上述输出只是一种情况。