数据结构——树的基本运算

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38908061/article/details/73476858

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.排序二叉树的创建和插入内容



二轮复习补充。

猜你喜欢

转载自blog.csdn.net/qq_38908061/article/details/73476858