数据结构--C语言版--二叉树

前序遍历:前序遍历

先序遍历的几种思考角度:

前序遍历:先根后叶

前序遍历:始终想着你走到这一步的时候,当前节点是谁的根?

前序遍历:找根(当前节点是谁的根?)

(比较喜欢这种思考角度)先序遍历是:先找左节点,然后就一直找左节点的左节点,然后再找左节点的左节点的左节点,…一直找的当前路径的尽头,实在没有左节点可以遍历了,
就找当前节点的兄弟节点,然后找兄弟节点的左节点,然后就一直找左节点,找到尽头(尽头指的是遍历到了这条路径的空节点),
再找尽头的右节点,

直到右节点也到了尽头(尽头指的是遍历到了这条路径的空节点),
就找最开始节点的右节点,
然后就一直左左左…左右

参考下面这张图:
在这里插入图片描述

在这里插入图片描述


中序遍历:
中序遍历


后序遍历:
后序遍历


补充一个概念:
度:一个节点包含的子树个数

度的示例
有一道题:要求判断一个二叉树是否为正则二叉树(正则二叉树不存在子树个数为1的节点)=>(即正则二叉树不存在度为1的结点)

解题思路:即正则二叉树的任意一个结点:要么度为0,要么度为2;
度为0的情况:=>①空树度为0.
代码表示:

if(!根节点) 
return "这一定是正则二叉树";

②叶子节点度为0
代码表示:

//T为当前遍历到的节点
if(T->lchild==NULL&&T->rchild==NULL)
return  "当前遍历到的节点,度为0";

度为2的情况:
(俗话说:“梅开二度”)

if(T->lchild!=NULL&&T->rchild!=NULL)
return "当前遍历到的节点,度为2";

度为0和度为2结合起来的代码为:

int Regular_binary_trees(BiTree T)//判断一棵树是否是正则二叉树
{
	if (!T) {
		return 1;//根节点为空,直接就是正则二叉树
	}

	if (!T->lchild && !T->rchild) {
		return 1;//(当前遍历到的节点)的左子树和右子树为空,直接不用往下遍历了,
		//这种情况下,度为0,(满足条件),本次递归返回真
	}//如果左右子树(但凡)有一个不为空,本次递归就往下遍历
	else if (T->lchild && T->rchild) {//如果左右子树都不为空("梅开二度"),就执行{}下面的语句,如果是一个为空,一个不为空,代码直接跳到1的位置
		if (Regular_binary_trees(T->lchild) && Regular_binary_trees(T->rchild)) {//继续遍历,
		//如果左子树要么度为0,要么度为2,
		//右子树也一样,要么度为0,要么度为2,
		//就认为这是一个正则二叉树,返回真.
			return 1;
		}
		else {
			//否则返回假
			return 0;
		}
	}
	else {位置1,(不满足条件),直接返回假,不是正则二叉树
	   //这个else对应这个if (T->lchild && T->rchild)
	   //这个{}表明:当前节点的左右子树有一个为空,一个不为空,那一定不是正则二叉树,返回假
		return 0;
	}
}

补充一个概念:满二叉树:
满二叉树有一道题:判断一个棵二叉树是否为满二叉树

解题思路:如果是空树,直接返回假;
如果不是空树,当前遍历到的节点要么度为0,要么度为2左右子树所拥有的 " 内部节点 " 数量一致;
度为0:是叶子节点
度为2:"相当于"这棵"可能是满二叉树"的内部节点


以上思路明显行不通,而且代码写起来十分复杂!

我们还是回归定义:
满二叉树的定义:一棵高度为h的满二叉树是具有2的h次幂-1(h>=0)个节点的二叉树,所以空树也是满二叉树
(注意:空树也是满二叉树)
由定义可知:
我们只需要求出高度h和节点总数sum2即可,
用一个for循环:

int sum1=1;
for(int j =1;j<=h;j++){
	sum1 =sum*2;
}

然后用sum1-1与sum2比较,如果相等即为满二叉树

全部代码如下:
(共需要三个函数)

//用三个函数来判断一颗树是否是满二叉树
//我们只需要求出高度h和节点总数sum2即可
int height(BiTree T) {
	int max;
	if (!T) return 0;
	int left = height(T->lchild);
	int right = height(T->rchild);
	if (left > right) {
		max = left;
	}
	else {
		max = right;
	}
	return max + 1;
}


//求节点的总数
int sum;//如果不放在全局区,sum的值是不会随着递归的次数增加而增加的
int count(BiTree T) {
	if (T) {
		sum += 1;
		count(T->lchild);
		count(T->rchild);
	}
	return sum;
}

void full_binary_tree(BiTree T)//判断一棵树是否是满二叉树
{
	//满二叉树的定义:一棵高度为h的满二叉树是具有2的h次幂-1(h>=0)个节点的二叉树,所以**空树也是满二叉树**
	//空树:                           高度为0          (2^0)-1==0   =>是满二叉树
	//只有一个根节点时:   高度为1          (2^1)-1==1   =>也是满二叉树
	if (!T) {
		printf("这是一颗空树.(同时也是一颗满二叉树)\n");
		//return 1;//空树也是满二叉树
	}
	else {//在不是空树的前提下运行以下代码

		int h = height(T);
		int sum2 = count(T);
		printf("这个棵树的高度h为:%d\n",h);
		int sum1 = 1;
		
		for (int i = 1; i <= h; i++) {
			sum1 = sum1 * 2;
		}
		printf("(sum-1):(2^h)-1为:%d\n",(sum1-1));
		printf("(sum2):总的节点个数:为%d\n",sum2);
		if (sum1 - 1 == sum2) {
			printf("这是一个非空的满二叉树!\n");
		}
		else {
			printf("这不是满二叉树!\n");
		}

	}
}

还有一道题:
求二叉树中值超过x的节点总数
(把前面几个题搞懂这个题就和切菜一样简单)

//定义在全局区的变量只有在程序结束后才会由系统释放
int exceed_x_count;//如果不放在全局区,exceed_x_count的值是不会随着递归的次数增加而增加的
int Exceed_X_Count(BiTree T, char x) {
	if (T) {

		if (T->data > x) {
			exceed_x_count++;
		}
		Exceed_X_Count(T->lchild, x);
		Exceed_X_Count(T->rchild, x);

	}

	return exceed_x_count;
}

还有一个题:
删除二叉树中以x为根节点的子树

//删除以x为根节点的子树=>此功能需要两个函数实现
void Free_Node(BiTree T) //后序遍历删除以bt为根的子树
{
	if (T)
	{
		Free_Node(T->lchild);
		Free_Node(T->rchild);
		printf("正在释放节点%c的内存(释放后不可再访问)!\n",T->data);
		free(T);
	}
}
void Delete_X_Node(BiTree T, char x) {
	if (T) {

		if (T->data == x) {//如果发现了目的节点,直接开始进行内存释放,然后直接结束掉该函数[Delete_X_Node(BiTree T, char x)]的进程
			Free_Node(T);
			return;
		}
		Delete_X_Node(T->lchild, x);
		Delete_X_Node(T->rchild, x);

	}
}

实现了以上全部功能的代码为:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>


typedef char ElemType;

typedef struct BiTNode {
	ElemType data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;

//请输入:
//AB.D..CE...//不是正则二叉树
//ABDH..I..E.J..CF.K..G..//不是正则二叉树
//ABD..E..C..//这是一个正则二叉树
//ABD..E..CF..G..//这是一个满二叉树

//创建一棵二叉树,利用前序遍历输入

CreateBiTree(BiTree* T) {
	//这里T为二级指针,即指针的地址,所以在main函数里面调用时,要传&指针

	char c;
	scanf_s("%c", &c);
	if ('.' == c) {
		*T = NULL;
	}
	else {
		*T = (BiTNode*)malloc(sizeof(BiTNode));
		(*T)->data = c;
		CreateBiTree(&(*T)->lchild);
		CreateBiTree(&(*T)->rchild);
	}
}

//访问二叉树节点的具体操作,要干什么?
//计数?  输出?
visit(ElemType c, int level) {
	printf("%c位于第%d层\n", c, level);
}

//前遍历二叉树(遍历输出)
void PreOrderTraver(BiTree T, int level) {
	//level表示层数(你进到这棵树的那一层了?)
	if (T) {

		visit(T->data, level);
		PreOrderTraver(T->lchild, level + 1);
		PreOrderTraver(T->rchild, level + 1);
		
		/*
		* 中序遍历:
		PreOrderTraver(T->lchild,level+1);
		visit(T->data,level);
		PreOrderTraver(T->rchild, level + 1);
		*/
		
		/* 后序遍历:
		PreOrderTraver(T->lchild,level+1);
		PreOrderTraver(T->rchild, level + 1);
		visit(T->data,level);
		*/
	}
}

//统计非叶子节点的个数
//定义在全局区的变量只有在程序结束后才会由系统释放
int Count ;//如果不放在全局区,Count的值是不会随着递归的次数增加而增加的
int NoLeafCount(BiTree T, int level) {
	if (T) {

		if (T->lchild !=NULL|| T->rchild != NULL) {
			Count++;
		}
		NoLeafCount(T->lchild, level + 1);
		NoLeafCount(T->rchild, level + 1);

	}
	
	return Count;
}

int Regular_binary_trees(BiTree T)//判断一棵树是否是正则二叉树
{
	if (!T) {
		return 1;//根节点为空,直接就是正则二叉树
	}

	if (!T->lchild && !T->rchild) {
		return 1;//(当前遍历到的节点)的左子树和右子树为空,直接不用往下遍历了,
		//这种情况下,度为0,(满足条件),本次递归返回真
	}//如果左右子树(但凡)有一个不为空,本次递归就往下遍历
	else if (T->lchild && T->rchild) {//如果左右子树都不为空("梅开二度"),就执行{}下面的语句,如果是一个为空,一个不为空,代码直接跳到1的位置
		if (Regular_binary_trees(T->lchild) && Regular_binary_trees(T->rchild)) {//继续遍历,
		//如果左子树要么度为0,要么度为2,
		//右子树也一样,要么度为0,要么度为2,
		//就认为这是一个正则二叉树,返回真.
			return 1;
		}
		else {
			//否则返回假
			return 0;
		}
	}
	else {位置1,(不满足条件),直接返回假,不是正则二叉树
	   //这个else对应这个if (T->lchild && T->rchild)
	   //这个{}表明:当前节点的左右子树有一个为空,一个不为空,那一定不是正则二叉树,返回假
		return 0;
	}
}

//用三个函数来判断一颗树是否是满二叉树
//我们只需要求出高度h和节点总数sum2即可
int height(BiTree T) {
	int max;
	if (!T) return 0;
	int left = height(T->lchild);
	int right = height(T->rchild);
	if (left > right) {
		max = left;
	}
	else {
		max = right;
	}
	return max + 1;
}


//求节点的总数
int sum;//如果不放在全局区,sum的值是不会随着递归的次数增加而增加的
int count(BiTree T) {
	if (T) {
		sum += 1;
		count(T->lchild);
		count(T->rchild);
	}
	return sum;
}

void full_binary_tree(BiTree T)//判断一棵树是否是满二叉树
{
	//满二叉树的定义:一棵高度为h的满二叉树是具有2的h次幂-1(h>=0)个节点的二叉树,所以**空树也是满二叉树**
	//空树:                           高度为0          (2^0)-1==0   =>是满二叉树
	//只有一个根节点时:   高度为1          (2^1)-1==1   =>也是满二叉树
	if (!T) {
		printf("这是一颗空树.(同时也是一颗满二叉树)\n");
		//return 1;//空树也是满二叉树
	}
	else {//在不是空树的前提下运行以下代码

		int h = height(T);
		int sum2 = count(T);
		printf("这个棵树的高度h为:%d\n",h);
		int sum1 = 1;
		
		for (int i = 1; i <= h; i++) {
			sum1 = sum1 * 2;
		}
		printf("(sum-1):(2^h)-1为:%d\n",(sum1-1));
		printf("(sum2):总的节点个数:为%d\n",sum2);
		if (sum1 - 1 == sum2) {
			printf("这是一个非空的满二叉树!\n");
		}
		else {
			printf("这不是满二叉树!\n");
		}

	}
}

//遍历超过x的节点个数
//定义在全局区的变量只有在程序结束后才会由系统释放
int exceed_x_count;//如果不放在全局区,exceed_x_count的值是不会随着递归的次数增加而增加的
int Exceed_X_Count(BiTree T, ElemType x) {
	if (T) {

		if (T->data > x) {
			exceed_x_count++;
		}
		Exceed_X_Count(T->lchild, x);
		Exceed_X_Count(T->rchild, x);

	}

	return exceed_x_count;
}


/// /
//删除以x为根节点的子树=>此功能需要两个函数实现
void Free_Node(BiTree T) //后序遍历删除以bt为根的子树
{
	if (T)
	{
		Free_Node(T->lchild);
		Free_Node(T->rchild);
		printf("正在释放节点%c的内存(释放后不可再访问)!\n",T->data);
		free(T);
	}
}
void Delete_X_Node(BiTree T, ElemType x) {
	if (T) {

		if (T->data == x) {//如果发现了目的节点,直接开始进行内存释放,然后直接结束掉该函数[Delete_X_Node(BiTree T, char x)]的进程
			Free_Node(T);
			return;
		}
		Delete_X_Node(T->lchild, x);
		Delete_X_Node(T->rchild, x);

	}
}

int main() {
	int level = 1;
	BiTree T = NULL;

	//printf("请输入:\n");
	CreateBiTree(&T);
	PreOrderTraver(T, level);//遍历输出

	printf("\n======================\n");
	printf("非叶子节点的个数为:%d", NoLeafCount(T,level));

	printf("\n======================\n");
	if (Regular_binary_trees( T )) {
		printf("这是一个正则二叉树\n");
	}
	else {
		printf("这个不是一个正则二叉树!\n");
	}

	printf("\n======================\n");

	//判断是否为满二叉树
	full_binary_tree(T);

	printf("\n======================\n");

	printf("比6大的结点总数为:%d\n",Exceed_X_Count(T, '6'));//示例:89.3..76...=>比6大的有三个

	system("pause");
	printf("\n======================\n");

	Delete_X_Node( T,  'B');//AB.D..CE...//不是正则二叉树

	//system("pause");
	return 0;
}


在这里插入图片描述


感谢大家的观看!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

踏过山河,踏过海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值