二叉树的创建及其相关操作

数据结构与算法 专栏收录该内容
3 篇文章 0 订阅

二叉树的创建及其相关操作

#include<stdio.h>
#include"malloc.h"
#define N 1000
typedef char DataType;

typedef struct bnode{
	DataType data;
	struct bnode * lchild, *rchild;
} Bnode,*BTree;

/*******二叉树的创建*********/
//算法6.11 创建二叉链表存储的二叉树
/*按照二叉树的遍历的思想即可*/
BTree   CreateBinTree()
{
	/* 以加入空结点的先序序列输入,构造二叉链表 */
	char ch;  
	BTree  t;
	ch = getchar(); //不断的从控制台获取数据 作为结点的数据域
	if (ch == '#')  t = NULL;   /*读入#时,将相应结点指针置空*/
	else
	{
		t = (BTree)malloc(sizeof(Bnode));  /* 生成结点空间 */
		t->data = ch;
		t->lchild = CreateBinTree();     /*构造二叉树的左子树 */
		t->rchild = CreateBinTree();     /*构造二叉树的右子树 */
	}
	return t;
}

//先序序列创建二叉树
/*这个算法做个扩展把,虽然我也会觉得晦涩*/
void CreateTree_Pre(BTree* T, DataType* str, int* n)
{
	DataType c = *(str + *n);

	*n = *n + 1;
	if (c == ';')
	{
		return;
	}
	if (c != '#')
	{
		*T = (BTree)malloc(sizeof(Bnode));
		(*T)->data = c;
		CreateTree_Pre(&((*T)->lchild), str, n);
		CreateTree_Pre(&((*T)->rchild), str, n);
	}
	else
	{
		*T = NULL;
	}
}

//利用二叉树的性质5建立二叉树
/*
BTree CreateBinTree1()
{
	BTree t=NULL,q;
	int i,j,x;
	BTree S[20]={0}; //辅助向量
	printf("\n i,x=");
	scanf("%d%d",&i,&x);
	while(i!=0 && x!=0)
	{
		q = (BTree)malloc(sizeof(Bnode));//产生一个新的结点
		//q = (struct node*)malloc(sizeof(struct node));  
		q->data=x;
		q->lchild = NULL;
		q->rchild = NULL;
		s[i]=t;
		if(i==1)
			t = q;
		else 
		{
			j=i/2;
			if((i%2)==0)
				s[j]->lchild=q;
			else
				s[j]->rchild=q;
		}
		printf("\n i.x=");
		scanf("%d%d"&i,&x);
	}
	return t;
}//循环结束的条件是输入的两个数据都为零
*/


//访问结点
void Visit(DataType data)
{
	printf("%-3c", data);
}

/*******d递归遍历*********/
//先序遍历
void PreOrder (BTree t){
	if(t){
		Visit(t->data);
		PreOrder (t->lchild);
		PreOrder (t->rchild);
	}
}

//中序遍历
void InOrder (BTree t){
	if(t){
		InOrder(t->lchild);
		Visit(t->data);
		InOrder(t->rchild);
	}
}


//后跟遍历
void PostOrder (BTree t){
	if(t){
		PostOrder(t->lchild);
		PostOrder(t->rchild);
		Visit(t->data);
	}
}

/*******非递归遍历*********/
/*利用栈代替系统栈进行遍历操作*/

//栈相关操作
#define MAXSIZE 1000
//利用顺序栈
typedef struct {
	BTree data[MAXSIZE]; //注意类型
	int top;
}SeqStack, * PSeqStack;

//初始化
PSeqStack Init_SeqStack()
{
	PSeqStack S;

	S = (PSeqStack)malloc(sizeof(SeqStack));
	if (S == NULL)
		printf("Init Fail!");
	else
		S->top = -1;
	return S;
}

//判栈空
int Empty_SeqStack(PSeqStack S)
{
	if (S->top == -1)
		return 1;
	else
		return 0;
}

//压栈
int Push_SeqStack(PSeqStack S, BTree x)
{
	if (S->top == MAXSIZE - 1)
		return 0;
	S->top++;
	S->data[S->top] = x;
	return 1;
}

//出栈
int Pop_SeqStack(PSeqStack S, BTree * x)
{
	if (Empty_SeqStack(S))
		return 0;
	*x = S->data[S->top];
	S->top--;
	return 1;
}

//先序遍历
/*
1.设置一个空栈
2.若二叉树不为空,则访问根节点,并将其指针入栈,然后遍历它的左子树
3.左子树遍历结束后,若栈不空,则将栈顶元素出栈,遍历栈顶元素的右子树,直到栈空为至

*/
void PreOrder_N(BTree t){
	PSeqStack   S;
	BTree   p = t;/*树初始化 */
	S = Init_SeqStack();/* 栈初始化*/
	while(p ||!Empty_SeqStack(S)) {/*循环条件:树非空、栈非空*/
		if(p){//如果二叉树非空
			Visit(p->data); /*二叉树不为空先访问根结点*/
			Push_SeqStack(S, p); /*访问完根结点,将结点压栈*/
			p = p->lchild;/*访问左子树*/
		}
		else{ //如果栈非空
			Pop_SeqStack(S, &p);  /*结点出栈*/
			p = p->rchild; /*访问结点的右子树*/
		}/*左子树为空,进右子树*/
	}
}

//中序遍历
/*
第一次遇到根结点不访问,而是去遍历它的左子树,再访问根
1.根指针入栈
2.遍历它的左子树
3.根出栈,访问根
4.遍历根的右子树
*/
void InOrder_N(BTree  t){
	PSeqStack   S;
	BTree p = t;
	S = Init_SeqStack();
	while (p || !Empty_SeqStack(S))
	{
		if (p) {//先不访问根
			Push_SeqStack(S, p);  //根入栈
			p = p->lchild;/*遍历左子树*/
		}
		else {
			Pop_SeqStack(S, &p);  //出栈
			Visit(p->data); //再访问根
			p = p->rchild; //再遍历其右子树
		}   /* 左子树为空, 进右子树 */
	}
}

/*后根遍历*/
/*后根遍历方法1:
(自右向左先序遍历二叉树)
1.利用先序遍历非递归的方法,对二叉树的根、右孩子、左孩子的顺序进行访问
2.访问到的根结点暂时不输出 ,而是保存到一个栈中
3.放问结束后,将被保存在栈中的结点输出即可
******注意:此算法需要两个栈******
*/

void PostOrder_1(BTree t)
{  //先用先序遍历入栈,再依次出栈就是后序遍历的结果
	PSeqStack S1;//存放最后结果的栈
	PSeqStack S2;//辅助栈空间,用于暂时存放在先序遍历过程中将经过的结点
	BTree p = t;	
	S1 = Init_SeqStack();
	S2 = Init_SeqStack();
	while(p||!Empty_SeqStack(S2))//注意是辅助栈不为空
	{
		if(p)  //条件不成立时为:根节点不存在右节点
		{
			//第一次遇到根结点时不需要访问
			Push_SeqStack(S1,p);   //保存到结果栈中
			Push_SeqStack(S2,p);   //将结点压入辅助栈中
			p=p->rchild;   //先右后左
		}
		else //辅助栈非空,且根节点不存在右子树
		{
			Pop_SeqStack(S2,&p); //出栈
			p=p->lchild; //遍历左子树
		}//右子树为空,进入左子树
	}
	while(!Empty_SeqStack(S1)) //注意这里的条件是结果栈非空  
	{   //将栈中结果依次出栈就是后序遍历的结果
		Pop_SeqStack(S1,&p);//出栈
		Visit((p->data));//依次访问结果栈中的结点
	}
}

/*后根遍历方法2:
   后跟遍历是在遍历过左右子树后再访问根结点,不同于先序遍历与中序遍历
后序遍历中,结点第一次出栈后还需要再次入栈,否则不能访问到根--入栈两次
出栈两次。
1.设置判断入栈次数的标志flag
2.将栈中的数据类型定义为指针和标志flag合并的结构体类型
****当结点指针入出栈的时候,标志也同时进出栈
3.结点第一次进栈时顺便将flag=0 一同带入栈栈,出栈后判断flag的值 
  (flag=0 第一次入栈,否则 第二次入栈)
4.结点第二次进栈,顺便将flag = 1带入。出栈同 步骤2判断
*/

//将栈中的数据类型定义为指针和标志flag合并的结构体类型
/*
typedef struct {
	Bnode * node; // 根结点  注意结点类型
	int  flag; //flag=0  第一次进/出栈,flag=1 第二次进出栈 
}DataType_1;  //结点和flag标记的封装在一块的结构体类型名


void PostOrder_2(BTree t)
{
	PSeqStack S;
	DataType_1 Sq;
	BTree p =t;
	S=Init_SeqStack();//栈的初始化
	while(p || !Empty_SeqStack(S))
	{
		if(p) //不成立条件是树根不存在左子树
		{
			Sq.flag=0; //
			Sq.node=p; //为第一次进栈做准备
			Push_SeqStack(S,Sq); // 将p指针以及flag压入栈中
			p=p->lchild; // 先遍历左子树
		}
		else //执行下面的语句的条件:是栈不为空 ,且根节点不存在左子树 p=NULL;
		{
			Pop_SeqStack(S,&Sq); //出栈
			p=Sq.node;  // 
			if(Sq.flag==0)//特征值为0,说明第一次出栈,还需要再次进栈
			{
				Sq.flag=1; //为第二次进栈做准备
				Push_SeqStack(S,Sq);  //再次将p指针以及flag压入栈中
				p=p->rchild; // 再遍历右子树
			}
			else//特征值为1 ,说明是第二次出栈
			{ 
				Visit(p->data);  //访问当前结点
				p=NULL;  // 表示当前结点处理完毕并为下次循环从栈中弹出结点做准备
			}
		}
	}
}
*/

/***********************************/
/*         二叉树的层次遍历        */
/***********************************/
/*
从上层到下层,每层中从左侧到右侧依次访问每个结点
1.访问根结点,并将根结点入队
2.当队列不为空时,重复下列操作
  1>从队列退出一个结点
  2>若其有左孩子,则访问左孩子,并将其左孩子入队
  3>若其有右孩子,则访问右孩子,并将其右孩子入队
*/

/*队列相关操作*/
typedef struct {
	BTree data[MAXSIZE];  //队列的存储空间
	int front;               //队头指针,通过元素所处位置来表示
	int rear;                //队尾指针
}SeqQueue, * PSeqQueue;


//队列初始化
PSeqQueue Init_SeqQuene()
{
	PSeqQueue Q;

	Q = (PSeqQueue)malloc(sizeof(SeqQueue));
	if (Q)
	{
		Q->front = 0;
		Q->rear = 0;
	}
	return Q;
}

//判断队空
int Empty_SeqQueue(PSeqQueue Q)
{
	if (Q && Q->front == Q->rear)
	{
		return 1;
	}
	return 0;
}

//判队列满
int Full_SeqQueue(PSeqQueue Q)
{
	if (Q && (Q->rear + 1) % MAXSIZE == Q->front)
	{
		return 1;
	}
	return 0;
}

//入队
int In_SeqQueue(PSeqQueue Q, BTree x)
{
	if (Full_SeqQueue(Q))
	{
		printf("队满");   //队满不能入队
		return -1;
	}
	Q->rear = (Q->rear + 1) % MAXSIZE;
	Q->data[Q->rear] = x;
	return 1;
}

//出队
int Out_SeqQueue(PSeqQueue Q, BTree * x)
{
	if (Empty_SeqQueue(Q))
	{
		printf("队空");   //队空不能出队
		return -1;
	}
	Q->front = (Q->front + 1) % MAXSIZE;
	*x = Q->data[Q->front];
	return 1;
}

//算法实现层次遍历二叉树
void LevelOrder(BTree  t)
{
	BTree p = t;
	PSeqQueue Q;
	if (p)//结点不为空时
	{
		Q = Init_SeqQuene();   //初始化队列
		Visit(p->data);//访问根结点
		In_SeqQueue(Q, p);  //并将根结点入队   
		while (!Empty_SeqQueue(Q))
		{
			//当队非空时重复执行下列操作
			Out_SeqQueue(Q, &p);    //出队
			if (p->lchild) //若其有左孩子
			{
				Visit(p->lchild->data); //则访问左孩子
				In_SeqQueue(Q, p->lchild);  //并将其左孩子入队
			}
			if (p->rchild)//若其有右孩子
			{
				Visit(p->rchild->data);//则访问右孩子
				In_SeqQueue(Q, p->rchild); //并将其右孩子入队
			}
		}
	}
}


/****************************************************/
/*         二叉树算法的应用实现                     */
/****************************************************/

/*
二叉树的相关应用算法的实现前,
一般都需要判断二叉树是否为空
*/

//课本算法6.7 计算二叉树的结点个数
/*在中序遍历(线序、后根)算法中对遍历到的结点进行计数*/
/*其实就是对遍历算法的稍加改造*/

/*
对于全局变量和局部变量得好好的理解,细节方面的知识点
很容易出现逻辑错误
*/
int count = 0;//count计数应该定义为全局变量 初始值为0;
void Count_Tree(BTree t)
{  //递归求解
	if (t)
	{
		Count_Tree(t->lchild); //递归遍历左孩子
		Visit(t->data);  //访问根结点
		count++; //每访问一次结点 就计数加一
		Count_Tree(t->rchild); //递归遍历右孩子
	}
}

//课本算法6.8 计算二叉树的结点个数
/*将二叉树看成是有树根、左子树、右子树三部分组成
  总的结点数就是三部分之和
  树根的节点数要么是1 要么是0(树不存在时)
  求左右子树结点的方法和求整棵二叉树的方法相同--递归
*/
int Count(BTree t)
{
	int lcount, rcount; //
	if (t == NULL) return 0; //当二叉树为空时根0 左0 右0 共0 
	else//当二叉树不为空时  (及树根结点为 1)
	{
		lcount = Count(t->lchild);  //递归计算左子树的结点(存在的话,具体过程不需要管)
		rcount = Count(t->rchild);//递归计算右子树的结点(存在的话,具体过程不需要管)
		return rcount + lcount + 1;  //(三者之和就是结点总数,然后返回给调用处)
	}
}

//课本算法6.9 计算二叉树的高度
/*
二叉树的高度就是左右子树的最大高度+1,所以得先求二叉树左右子树的高度
求解方法:同求解整棵二叉树的高度解法一致--递归
*/
int Height(BTree  t)
{   //左右子树的最大高度加1
	int  h1, h2;
	if (t == NULL)  //如果二叉树为空  
		return 0;  //高度为0
	else  //二叉树存在
	{
		h1 = Height(t->lchild);/*求左子树的高度*/
		h2 = Height(t->rchild);/*求右子树的高度*/
		if (h1 > h2)   //如果左子树的高度大于右子树
			return h1 + 1; //返回左子树的高度+1 及为树的高度
		else      //如果右子树的高度大于右子树
			return  h2 + 1;//返回右子树的高度+1 及为树的高度
		//return 1+(h1>h2?h1:h2);  //这里用条件表达式更方便点
	}
}

//课本算法6.10 二叉树的复制
/*
二叉树的基本元素:根结点、左子树、右子树
因此,赋值二叉树就是复制二叉树的三个基本元素
  采用后序遍历的思想:
	1.先复制左右子树
	2.后复制根结点
	3.最后返回二叉树根的地址
*/
BTree CopyTree(BTree t)
{   //递归左右子树
	BTree  p, q, s;
	if (t == NULL)  //先判断二叉树是否存在
		return (NULL);
	p = CopyTree(t->lchild);    /*复制左子树*/
	q = CopyTree(t->rchild);    /*复制右子树*/
	/*复制根结点*/
	s = (Bnode*)malloc(sizeof(Bnode)); //先申请一个新的结点
	s->data = t->data;  //赋值根结点的数据域
	s->lchild = p;    
	s->rchild = q;   //复制根节点的左右子树
	return s;  //最后返回根的地址
	/*注:s的类型为结点的指针类型*/
}

//算法6.12 求二叉树中每层结点个数
/*先序遍历和中序遍历时都是从一个结点向他的左右孩子移动的
如果当前结点位于L层,则它的左右孩子在L+1 层
所以在遍历算法中给当前访问的到的结点增设一个指示该结点所位于的层数变量L,
1.设二叉树的高度为H,数组num[],初始值为0,
2.num[i]表示第i层上的结点个数。
*/
void  Levcount(BTree  t, int  L, int num[])
/* 求链式存储的二叉树t中每层结点个数
   L表示当前t所指结点的层次,
   当t初值为根时,L初值为1,num数组元素初始化0 
*/
{
	if (t)
	{
		Visit(t->data); //访问当前结点
		num[L]++;  //当前t所指结点的层次数增加1
		Levcount(t->lchild, L + 1, num); //递归左子树
		Levcount(t->rchild, L + 1, num);//递归右子树
	}
}
/*******************************叶子结点************************************/
//统计二叉树的叶子结点的数量
/*  叶子结点:没有左子树也没有右子树的结点
1.先设置一个计数器,记录叶子结点的数量m
2.遍历过程中,判断是否为叶子结点
  是: m+1; 
3.直到遍历结束,就可以得到叶子结点的数量了
<这里用先序遍历方法递归实现>
*/
void numfleaf(BTree t , int &m)
{/*这里的m是引用型形参,每一次递归调用前都可以实时记录叶子结点的个数*/
	if(t){
		if(t->lchild==NULL && t->rchild==NULL){/*左右子树为空*/
			m++;  /*叶子结点计数器加1*/
		}
		numfleaf(t->lchild,m); 
		numfleaf(t->rchild,m);//递归遍历左右子树
	}
}
/*
//主调函数示例:
main()
{
	BTree t;
	int m;
	t=CreateBinTree();//先建立二叉树;
	m=0; //全局变量m置初始值
	numfleaf(t ,m);//求t树叶子结点的个数
	printf("\n叶子结点的个数:m = %d\n",m);  //输出结果
}
*/
/*******************************叶子结点************************************/

//算法实现左右孩子交换
void Exchange(BTree t)//算法实现左右孩子交换
{
	BTree p;
	if(t)
	{
		p=t->lchild;
		t->lchild=t->rchild;
		t->rchild=p;
		Exchange(t->lchild);
		Exchange(t->rchild);
	}
}

// 递归在二叉树中求位于先序序列中第k位置的结点值
int c;  //计数器c作为全局变量处理,初始值为0
void Get_PreSeq(BTree T, int k) //线序序列为k的结点的值
{
	if(T)
	{
		c++; //每访问一个子树的根都会使线序序号计数器加1
		if(c==k)
		{
			printf("\n指定结点的值是%d\n",T->data);
			//exit(1);
		}
		else
		{
			Get_PreSeq(T->lchild,k); //在左子树中查找
			Get_PreSeq(T->rchild,k); //在右子树中查找
		}
	}
}


/*int main()
{
	BTree t;
	int m;
	t=CreateBinTree();//先建立二叉树;
	m=0; //全局变量m置初始值
	numfleaf(t ,m);//求t树叶子结点的个数
	printf("\n叶子结点的个数:m = %d\n",m);  //输出结果
	return 0 ; 
}
*/



void main()
{
	char s[N];
	BTree t;
	int n = 0;
	int c[N] = { 0 }, i;
	int m;
	int k;

	//方法1:
	//scanf("%s", s); //测试数据ABD#G###CE##FH###;
	//方法2:
	//CreateTree_Pre(&t, s, &n);
	t = CreateBinTree();
	/*********************************************/
	printf("\n***********递归遍历二叉树***********\n");
	printf("\n先序遍历二叉树:\n");
	PreOrder(t);
	printf("\n中序遍历二叉树:\n");
	InOrder(t);
	printf("\n后序遍历二叉树:\n");
	PostOrder(t);
    /*********************************************/
	printf("\n***********非递归遍历二叉树***********\n");
	printf("\n先序非递归遍历二叉树:\n");
	PreOrder_N(t);
	printf("\n中序非递归遍历二叉树:\n");
	InOrder_N(t);
	printf("\n后序非递归遍历二叉树:\n");
    PostOrder_1(t);
	printf("\n层次遍历二叉树:\n");
	LevelOrder(t);

    /****************************************************/
	printf("\n***********二叉树应用算法实例***********\n");
	Count_Tree(t);//计算结点的个数
	printf("\n结点个数:%d, %d\n",count, Count(t));
	/*这里输出了两个计算结点个数方法的结果值*/

	n = Height(t);//计算树的高度
	printf("\n树的高度%d\n", n);

	Levcount(t, 1, c);  //计算每一层结点的个数
	printf("\n");
	for (i = 1; i <= n; i++)
	{
		printf("层:%d,结点数:%d\n", i, c[i]);
	}

    m=0; //全局变量m置初始值
	numfleaf(t ,m);//求t树叶子结点的个数
	printf("\n叶子结点的个数:m = %d\n",m);

	k = 2;  //先默认一个值,可以换换试试
	Get_PreSeq(t,k); //线序序列为k的结点的值
	/*函数里有输出语句,所以这里不printf()了*/

	Exchange(t);//算法实现左右孩子交换
	printf("\n交换左右孩子后层次遍历二叉树:\n");
	LevelOrder(t);//层遍历输出交换左右孩子的树
	printf("\n");
}
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值