1.二叉树的建立和基本操作
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char ElementType;
//二叉树的链式存储结构定义
typedef struct BTreeNode{
char data;
struct BTreeNode *Lchild,*Rchild;
}tree;
//用 先序遍历序列(遍历到的空子树用#表示) 构造
tree *CreatTree1(tree *T)
{
ElementType ch;
scanf("%c",&ch);
if(ch=='#') return NULL;
else{
T=(tree*)malloc(sizeof(tree));
T->data=ch;
T->Lchild=CreatTree1(T->Lchild);
T->Rchild=CreatTree1(T->Rchild); //如果用中序、后序构造,只需把赋值和创建左右子树顺序换一下
return T;
}
}
//用 括号表示法(广义表) 构造
void CreatTree2(tree *&T)
{
char ch[20];
tree *sta[100],*p;
int top=-1,i=0,flag=0;
T=NULL;
gets(ch);
while(ch[i]!='\0')
{
switch(ch[i])
{
case '(':
flag=1;
top++;
sta[top]=p;
break;
case ',':
flag=2;
break;
case ')':
top--;
break;
default:
p=(tree*)malloc(sizeof(tree));
p->data=ch[i];
p->Lchild=NULL;
p->Rchild=NULL;
if(T==NULL)
T=p;
else{ //此处也在default条件下,输入信息不是(),时,才进行操作
if(flag==1)
sta[top]->Lchild=p;
if(flag==2)
sta[top]->Rchild=p;
}
}
i++;
}
}
//用顺序树(空位用#表示)建树
tree *CreatTree3(char* str, int pose, int size)
{
char ch;
tree * T;
char * p=str;
ch = p[pose];
if(ch=='#'|| pose>=size)
return NULL; // 表示空结点
else
{
T=(tree *)malloc(sizeof(tree)); //非空则构造新结点
T->data=ch; //新结点数据域即为读入字符
T->Lchild=CreatTree3(p, 2*pose+1,size); //建立左子树
T->Rchild=CreatTree3(p, 2*pose+2,size); //建立右子树
}
return T;
}
//销毁二叉树
void DestroyTree(tree *&T) //此处用*& 表示取此指针地址,在函数内部对指针做的改变(T=NULL)可以应用于函数外,避免出现野指针
{
if(T==NULL) return;
else {
DestroyTree(T->Lchild);
DestroyTree(T->Rchild);
free(T);
T=NULL;
}
//写法二 便于理解
// if(T->Lchild!=NULL)
// DestroyTree(T->Lchild);
// if(T->Rchild!=NULL)
// DestroyTree(T->Rchild);
// if(T->Lchild==NULL && T->Rchild==NULL)
// {
// free(T);
// T=NULL;
// }
}
//用括号表示法输出二叉树 (即递归先序输出,只是需要加上括号和逗号表示位置)
void PrintTree(tree * T)
{
if(T!=NULL)
{
printf("%c",T->data);
if(T->Lchild!=NULL || T->Rchild!=NULL)
{
printf("(");
PrintTree(T->Lchild);
if (T->Rchild!=NULL) printf(",");
PrintTree(T->Rchild);
printf(")");
}
}
}
//先序遍历
void PreOrderTraverse(tree *T)
{
if(T==NULL)return;
printf("%c ",T->data);
PreOrderTraverse(T->Lchild);
PreOrderTraverse(T->Rchild);
}
//中序遍历
void InOrderTraverse(tree *T)
{
if(T==NULL)return;
InOrderTraverse(T->Lchild);
printf("%c ",T->data);
InOrderTraverse(T->Rchild);
}
//后序遍历
void PostOrderTraverse(tree *T)
{
if(T==NULL)return;
PostOrderTraverse(T->Lchild);
PostOrderTraverse(T->Rchild);
printf("%c ",T->data);
}
//层次遍历
void LevelOrderTraverse(tree * T)
{
tree *p,* queue[100];
int front,rear;
front=rear=0;
rear++;
queue[rear]=T; //层次遍历要每读取一个数,就把其左右子树放到最后方,继续之前的后续操作
while(rear!=front) //不影响顺序读取兄弟节点,同时储存后面的信息,显然队列的先进先出模式最适合这种结构,依次读取节点
{
front=(front+1)%100;
p=queue[front];
if(p->Lchild!=NULL) //当子树不为空时,将子树“排队”
{
rear=(rear+1)%100;
queue[rear]=p->Lchild;
}
if(p->Rchild!=NULL)
{
rear=(rear+1)%100;
queue[rear]=p->Rchild;
}
printf("%c ",p->data);
}
}
//计算节点数
int node(tree *T)
{
if(T==NULL)return 0;
return node(T->Lchild)+node(T->Rchild)+1;
}
//计算叶子节点数
int LeafNode(tree *T)
{
if(T==NULL) return 0;//易忘,缺少这句话会使结果出错
else if(T->Lchild==NULL && T->Rchild==NULL) return 1;
else return LeafNode(T->Lchild)+LeafNode(T->Rchild);
}
int main(){
tree * T;
int flag=1;
while(flag!=0)
{
printf("====================================\n");
printf("请按以下提示操作:\n"
"0.退出程序\n"
"1.先序建树(空子树用#)\n"
"2.括号表示法建树\n"
"3.顺序树建树(空子树用#)\n"
"4.以括号表示法输出\n"
"5.删除二叉树\n"
"6.先序遍历\n"
"7.中序遍历\n"
"8.后序遍历\n"
"9.层次遍历\n"
"10.输出叶子节点个数\n");
scanf("%d",&flag);
getchar(); /* 注意:调用建树函数中有scanf("%c")时,会读取到输入flag时的换行信息,导致出错,此处加上getchar防止此类错误出现 */
printf("====================================\n");
switch(flag)
{
case 0:
return 0;
break;
case 1:
printf("====================================\n");
T=CreatTree1(T);
break;
case 2:
CreatTree2(T);
break;
case 3:
char a[20];
gets(a);
T=CreatTree3(a,0,strlen(a));
break;
case 4:
PrintTree(T);
printf("\n") ;
break;
case 5:
DestroyTree(T);
break;
case 6:
PreOrderTraverse(T);
printf("\n") ;
break;
case 7:
InOrderTraverse(T);
printf("\n") ;
break;
case 8:
PostOrderTraverse(T);
printf("\n") ;
break;
case 9:
LevelOrderTraverse(T);
printf("\n");
break;
case 10:
printf("%d",LeafNode(T));
printf("\n");
break;
default:
printf("您输入了错误值");
}
}
return 0;
}
2.其他一些简单操作
a.转换左右子树,并生成新树:
void Turn(tree *T,tree *&B) //此处对B树要用*&调用的同时改变本身,用这种思想在递归的过程中创建子树
{
if(T==NULL)B=NULL;
else{
B=(tree *)malloc(sizeof(tree));
B->data=T->data;
Turn(T->Lchild,B->Rchild); //递归交换左右子树
Turn(T->Rchild,B->Lchild);
}
}
b.已知先序或者后序遍历中的一种与中序遍历,构造一颗确定的二叉树
首先明确一个定义:已知中序序列和先序或者后序序列中的任意一个,便可以构造唯一确定的二叉树;但是如果只确定先序序列与后序序列,则不行。
算法首要的目的是在中序遍历中找到先序遍历的第一个值,即二叉树的根,在中序遍历的位置,利用字符串相减得到位置K,代码实现如下:
int main() {
char s1[20] ,s2[20] ,*s ; //S1是中序遍历的串,S2是先序遍历的串
int k;
printf("请输入中序遍历:");
gets(s1);
printf("请输入先序遍历:");
gets(s2);
s=s1;
for(;s<s1+20;s++) //S指针依次检测中序串,在其中查找与根节点相同节点的位置
if(*s==*s2)
break;
k=s-s1; //此处两串相减代表指针的地址差 ,k值就是中序遍历中根节点的位置
//循环部分也可以直接用数值i带入循环,那么i就是这里的k,下面函数中用的这种方法
printf("右子树中序遍历为:");
puts(s1+k+1);
printf("%d",k);
return 0;
}
大问题:整个串的先序遍历,整个串的中序遍历 f(s1,s2,n)
小问题:左子树的先序遍历、左子树的中序遍历,右子树的先序遍历,右子树的中序遍历 f(Ls1,Ls2,k) f(Rs1,Rs2,n-k-1)
代码实现:
tree *CreatTree0(char * pre,char * in,int n)
{
tree *T;
int i;
if(n==0) return NULL; //递归出口
for(i=0;i<n;i++)
if(*(in+i)==*pre)
break;
T=(tree *)malloc(sizeof(tree));
T->data=*pre;
T->Lchild=CreatTree0(pre+1,in,i); // 这里的i带入,递归时只判断前i项,也就是左子树部分长度
T->Rchild=CreatTree0(pre+i+1,in+i+1,n-i-1); //pre+i+1就是右子树 部分,n-i-1对应右子树的长度
return T;
}
利用上述思路,同样可以用后序和中序遍历构造树。
C.查找二叉树某一层的节点数
对于某一层的节点数查找,可以借鉴递归遍历的思想,但是在查找过程中,需要知道当前所在的节点层数。
如果所在节点层数和需要查找的在同一层,则n++;依次递归。
void TreeFloor(tree *T,int h,int k,int &n)//注意最后一项的n前面的&符号,保证在递归的过程中统计n的值。此处也可以设置全局变量
{
if(T==NULL) return;
if(h==k)n++;
if(h<k)
{
TreeFloor(T->Lchild,h+1,k,n);
TreeFloor(T->Rchild,h+1,k,n);
}
}
D.排序二叉树的创建和插入内容
二轮复习补充。