【数据结构】二叉树

目录

一.二叉树的概念及性质

1.二叉树的概念

2.二叉树的性质

3.特殊的二叉树

二.二叉树的链式结构及遍历

1.二叉树的链式结构

2.二叉树的前、中、后序遍历

3.二叉树的层序遍历

三.二叉树的基础操作实现

1.二叉树的销毁

2.二叉树的总节点个数

3.二叉树的叶节点个数

4.二叉树第K层节点个数

5.BinaryTreeFind - 在二叉树中查找指定值

6.判断是否是完全二叉树

7.通过前序遍历的数组"124##5#8##36##7##"构建二叉树

8.二叉树的最大深度

9.二叉树的最小深度


一.二叉树的概念及性质

1.二叉树的概念

节点的度:一个节点含有的子树的个数称为该节点的度(二叉树每个节点的度最大为2,最小为0)

叶节点:度为0的节点

分支节点:度不为0的节点

父节点、子节点:若一个节点含有子节点,则这个节点称为该子节点的父节点;若一个节点含有父节点,则这个节点成为该父节点的子节点

深度:二叉树的层数,一般情况下将第一层的层数看作1(因为如果第一层的层数看作0,空树的层数又如何定义?)

1.二叉树不存在度大于2的节点。

2.二叉树的子树有左右之分,次序不可颠倒,因此二叉树是有序树

注意:二叉树的主要用途是搜索二叉树,而不是存储数据。

2.二叉树的性质

若规定根节点的层数为1,则第i层最多有2^(i-1)个节点

若规定根节点的层数为1,则深度为h的二叉树最多有2^h-1个节点

任何一棵非空二叉树,度为0的节点永远比度为2的节点多一个,p0 = p2 + 1

若规定根节点的层数为1,n个节点的满二叉树深度:h=log2(n+1),log以2为底,n+1的对数

对于具有n个节点的完全二叉树,如果按照从上到下,从左到右的数组顺序对所有节点从0开始编号

则对序号为i位置的节点有:

若i大于0,i位置的父节点:(i - 1) / 2

i位置的左子节点:2i + 1

i位置的右子节点:2i + 2

3.特殊的二叉树

完全二叉树

假设一棵二叉树的深度为h,前h-1层符合一棵满二叉树,最后一层必须从左向右依次存放,那么这就是一棵完全二叉树。

完全二叉树的节点个数不确定,一棵深度为h的完全二叉树节的节点个数范围:2^(h-1) ~ 2^h-1

解释:一棵h-1层的满二叉树节点个数为2^(h-1)-1,第h层最少有1个节点,最多有2^(h-1)个节点。

所以假设总结点个数为X,那么 2^(h-1)-1+1 <= X <= 2(h-1)-1+2^(h-1),结果是2^(h-1) <= X <= 2^h-1

满二叉树

满二叉树是一种特殊的完全二叉树。

一棵二叉树的每一层节点个数都是该层最大值,那么这棵二叉树就称为满二叉树。

换种说法,如果一棵二叉树的深度为h,且这棵二叉树的节点个数为2^h-1,那么这就是一棵满二叉树。

一句话总结,完全二叉树和满二叉树是特殊的二叉树,而满二叉树又是特殊的完全二叉树。

二.二叉树的链式结构及遍历

1.二叉树的链式结构

这一块随意构建好一个二叉树,后面所有代码都基于这个二叉树实现。

typedef int BTDataType;
typedef struct BinaryTree
{
    BTDataType val;               //存储数据
    struct BinaryTree* leftchild; //左孩子指针
    struct BinaryTree* rightchild;//右孩子指针
}BTNode*;

//创建二叉树节点
BTNode* BuyNewNode(BTDataType x)
{
	BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
	if (tmp == NULL)
	{
		printf("malloc failed!\n");
		exit(0);
	}
	BTNode* newnode = tmp;
	newnode->val = x;
	newnode->leftchild = newnode->rightchild = NULL;
	return newnode;
}

//构建好一棵二叉树
BTNode* A = BuyNewNode(1);
BTNode* B = BuyNewNode(2);
BTNode* C = BuyNewNode(3);
BTNode* D = BuyNewNode(4);
BTNode* E = BuyNewNode(5);
BTNode* F = BuyNewNode(6);
BTNode* G = BuyNewNode(7);

A->leftchild = B;
A->rightchild = C;
B->leftchild = D;
B->rightchild = E;
E->leftchild = F;
C->rightchild = G;

2.二叉树的前、中、后序遍历

前序遍历

//前序遍历
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("%c ", '#');
		return;
	}
	//打印当前根节点的值
	printf("%d ", root->val);
	//递归左子树
	PrevOrder(root->leftchild);
	//递归右子树
	PrevOrder(root->rightchild);
}

中序遍历

//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("%c ", '#');
		return;
	}
	//递归左子树
	InOrder(root->leftchild);
	//打印当前根节点的值
	printf("%d ", root->val);
	//递归右子树
	InOrder(root->rightchild);
}

后序遍历

//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("%c ", '#');
		return;
	}
	//递归左子树
	PostOrder(root->leftchild);
	//递归右子树
	PostOrder(root->rightchild);
	//打印当前根节点的值
	printf("%d ", root->val);
}

3.二叉树的层序遍历

二叉树的层序遍历需要借助队列来实现,每出一次队列就要判断将要出队列的这个节点有无左右孩子节点,

如果有,就要将其左右孩子节点带入到队列中。

void BinaryTreeLevelOrder(BTNode* root)
{
	assert(root);
	//创建一个队列
	Queue q;
	QueueInit(&q);
	//先把根节点入队列
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* first = QueueTop(&q);
		printf("%d ", first->val);
		QueuePop(&q);
		//如果队头的左节点不为空,就带入队列
		if (first->leftchild != NULL)
		{
			QueuePush(&q, first->leftchild);
		}
		//如果队头的右节点不为空,就带入队列
		if (first->rightchild != NULL)
		{
			QueuePush(&q, first->rightchild);
		}
	}
	QueueDestory(&q);
}

三.二叉树的基础操作实现

1.二叉树的销毁

二叉树的销毁建议使用后序遍历依次释放,前序和中序都不可取,因为释放了父节点就没有办法去找他的子节点。

void BinaryTreeDestory(BTNode* root)
{
	if (root)
	{
		//采用后序的方式依次释放节点
		BinaryTreeDestory(root->leftchild);
		BinaryTreeDestory(root->rightchild);
		free(root);
	}
}

2.二叉树的总节点个数

int BinaryTreeSize(BTNode* root)
{
	//如果根节点不为空,就把左子树和右子树的节点的和加一(意为加上自己)
	return root == NULL ? 0 : BinaryTreeSize(root->leftchild) + BinaryTreeSize(root->rightchild) + 1;
}

3.二叉树的叶节点个数

int BinaryTreeLeapSize(BTNode* root)
{
	//节点不存在
	if (root == NULL)
	{
		return 0;
	}
	//是叶节点
	if (root->leftchild == NULL && root->rightchild == NULL)
	{
		return 1;
	}
	//左子树的叶节点数+右子树的叶节点数
	return BinaryTreeLeapSize(root->leftchild) + BinaryTreeLeapSize(root->rightchild);
}

4.二叉树第K层节点个数

我们所说的第K层节点个数,第K层相对对象是谁?

比如说第3层,肯定是相对于第1层来说,这时第1层是第1层,向后数到的第3层,

那么相对于第2层来说,第2层是第1层,我们刚刚说的第3层就是第2层,因为相对于第2层,第2层成了第1层

按照这个思路,求第K层,可以理解为:

求第1层的第K层,交给第2层的K-1层,交给第3层的K-2层 ... 交给第n层的K-n层,

当K-n等于1时,说明交给了第n层的第1层,这就是要求这一层的节点呀!这时就可以返回了,K-n == 1时(k = K-n),就是返回条件。

k:相对第n层的层数

K:要求的层数

n:k相对第几层,1,2,3,4,5 ...

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	//节点不存在
	if (root == NULL)
	{
		return 0;
	}
	//节点存在,且是要求的第K层
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->leftchild, k - 1) 
		 + BinaryTreeLevelKSize(root->rightchild, k - 1);
}

递归展开图:

5.BinaryTreeFind - 在二叉树中查找指定值

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	//节点不存在
	if (root == NULL)
		return NULL;
	if (root->val == x)
		return root;
	//递归左
	BTNode* ret1 = BinaryTreeFind(root->leftchild, x);
	//如果左子树找到值就返回
	if (ret1)
		return ret1;
	//递归右
	BTNode* ret2 = BinaryTreeFind(root->rightchild, x);
	//如果左子树找到值就返回
	if (ret2)
		return ret2;
	//都没找到返回空
	return NULL;
}

6.判断是否是完全二叉树

层序遍历,这次的层序遍历把空节点也遍历进去,

如果遍历到空节点,那么这个节点之后的节点都应该是空,

如果出现非空节点,则不是完全二叉树。

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		//取头节点
		BTNode* front = QueueTop(&q);
		QueuePop(&q);
		//当取到空节点时,跳出循环
		if (front == NULL)
		{
			break;
		}
		//带入左右孩子,不管是否为空
		QueuePush(&q, front->leftchild);
		QueuePush(&q, front->rightchild);
	}
	//看后续是否全部为空节点
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueTop(&q);
		QueuePop(&q);
		//出现不为空节点,直接否定
		if (front != NULL)
		{
			QueueDestory(&q);
			return false;
		}
	}
	QueueDestory(&q);
	return true;
}

7.通过前序遍历的数组"124##5#8##36##7##"构建二叉树

因为给了空节点的位置,所以才能只通过前序遍历来确定一颗二叉树。

如果不给空节点位置的话,需要中序+前序或者后序+前序,才可以确定一棵二叉树

// 通过前序遍历的数组"124##5#8##36##7##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	//是空节点就返回空
	if (a[*pi] == '#')
	{
		//向后走一次
		(*pi)++;
		return NULL;
	}
	//创建根节点
	BTNode* root = BuyNewNode(a[(*pi)++]);
	//递归创建左子树
	root->leftchild = BinaryTreeCreate(a, pi);
	//递归创建右子树
	root->rightchild = BinaryTreeCreate(a, pi);
	//返回根节点
	return root;
}

8.二叉树的最大深度

//求二叉树的最大深度
int BinaryTreeMaxDepth(BTNode* root)
{
	//空节点返回0
	if (root == NULL)
		return 0;
	//递归求左子树深度
	int leftDepth = BinaryTreeMaxDepth(root->leftchild);
	//递归求右子树深度
	int rightDepth = BinaryTreeMaxDepth(root->rightchild);
	//选左右子树深度最大的一个并且加上本层,返回给上一层。
	return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}

9.二叉树的最小深度

//求二叉树的最小深度
int BinaryTreeMinDepth(BTNode* root)
{
	//空节点返回0,这种情况其实只有在这个树为空的时候才会发生
	if (root == NULL)
		return 0;
	//叶子节点返回1
	if (root->leftchild == NULL && root->rightchild == NULL)
		return 1;
	//定义最小深度变量
	int minDepth = INT_MAX;
	//找二叉树的最小深度不能随意递归空节点,因为在比较的时候空节点深度是0,会影响最小深度值。
	if (root->leftchild != NULL)
	{
		int getleft = BinaryTreeMinDepth(root->leftchild);
		minDepth = getleft < minDepth ? getleft : minDepth;
	}
	if (root->rightchild != NULL)
	{
		int getright = BinaryTreeMinDepth(root->rightchild);
		minDepth = getright < minDepth ? getright : minDepth;
	}
	return minDepth + 1;
}

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值