二叉树链式结构的实现

目录

1.二叉树链式结构的实现

1.1前置说明

1.2二叉树的遍历

1.2.1前序、中序以及后序遍历

1.2.2层序遍历

1.3二叉树节点个数及高度等

1.4二叉树的创建和销毁

1.5完全二叉树的判断 


1.二叉树链式结构的实现

1.1前置说明

在学习二叉树的基本操作前,需先要创建一棵二叉树此处手动快速创建一棵简单的二叉树,二叉树真正的创建方式在文末给出。

typedef struct BTNode
{
	struct BTNode* left;
	struct BTNode* right;
	DataType data;
}BTNode;




BTNode* BinaryTreeCreate()
{
	BTNode* node1 = BuyBTNode(1);
	BTNode* node2 = BuyBTNode(2);
	BTNode* node3 = BuyBTNode(3);
	BTNode* node4 = BuyBTNode(4);
	BTNode* node5 = BuyBTNode(5);
	BTNode* node6 = BuyBTNode(6);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;

	return node1;
}

注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式文末说明 

1.2二叉树的遍历

1.2.1前序、中序以及后序遍历

所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历 是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

 按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历: 

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。(根节点——>根的左子树——>根的右子树)

2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。(根的左子树——>根节点——>根的右子树)

3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。(根的左子树——>根的右子树——>根节点)

 由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为 根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

 二叉树的遍历等操作等都要借助二叉树的概念:

        1.空树

        2.非空:根节点,根节点的左子树、根节点的右子树组成的。

 

//前序遍历  根节点-->根的左子树-->根的右子树
void PreOrder(BTNode* root)
{
	if (root == NULL)
		return;
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}
//中序遍历   根的左子树-->根节点-->根的右子树
void InOrder(BTNode* root)
{
	if (root == NULL)
		return;
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
//后序遍历    根的左子树-->根的右子树-->根节点
void PostOrder(BTNode* root)
{
	if (root == NULL)
		return;
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

 前序遍历递归图解

 遍历结果如图:

1.2.2层序遍历

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在 层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层 上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

//层序遍历

void BinaryLevelOrder(BTNode* root)
{
	Queue q;
	if (NULL == root)
		return;
	QueueInit(&q);
	//将根节点入队列,当队列不空时,循环下列操作
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		struct BTNode* cur = QueueFront(&q);
		//遍历当前节点
		printf("%d ", cur->data);
		//如果当前节点有左孩子,则入队列;有右孩子入队列
		if (cur->left)
			QueuePush(&q, cur->left);
		if (cur->right)
			QueuePush(&q, cur->right);
		//遍历当前节点后删除改节点
		QueuePop(&q);
	}
	QueueDestroy(&q);
	printf("\n");

}

 

1.3二叉树节点个数及高度等

以以下这个二叉树为例:

 以下操作都会利用到二叉树的概念,所以牢记二叉树的概念十分重要。

//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (NULL == root)
		return 0;
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (NULL == root)
		return 0;
	//如果当前节点没有左右孩子则为叶子节点
	if (NULL == root->left&&NULL == root->right)
		return 1;

	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

//二叉树的高度
int BinaryTreeHeight(BTNode* root)
{
	if (NULL == root)
		return 0;
	//先求root子树的高度
	int leftHeight = BinaryTreeHeight(root->left);
	int rightHeight = BinaryTreeHeight(root->left);
	//root树的高度为较高的子树的高度+1
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

//二叉树第k层的节点个数
int BinaryTreeLevelKSize(BTNode* root,int k)
{
	if (NULL == root || k <= 0)
		return 0;
	//第一层节点数为1
	if (1 == k)
		return 1;
	//第k层节点数不知道,递归在root子树中求k-1层的节点数
	return BinaryTreeLeafSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);

}

//查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, DataType x)
{
	BTNode* ret = NULL;
	if (NULL == root)
		return NULL;
	if (x == root->data)
		return root;
	//如果根的值域不是要找的x,递归到根的左右子树中寻找
	(ret = BinaryTreeFind(root->left, x)) || (ret = BinaryTreeFind(root->right, x));
	return ret;
}

 

1.4二叉树的创建和销毁

 二叉树创建时需要在序列中体现节点孩子的实际情况,用'#’表示某节点的子树不存在,根据二叉树的定义,递归创建二叉树

int array[] = { 1, 2, 3, '#', '#', '#', 4, 5, '#', '#', 6 };

 

//二叉树的创建

// array保存的是:用户要创建二叉树时节点中值域的集合
// size: array中有效元素的个数
// pindex: 指向外部用来表示array下标的index
// invalid: 表示array数组中用来区分节点是否存在子树的标记
BTNode* _BinaryTreeCreate(DataType array[], int size, int* pindex, char invalid)
{
	BTNode* root = NULL;
	if (*pindex < size&&array[*pindex] != invalid)
	{
		//创建根节点
		root = BuyBTNode(array[*pindex]);
		//递归创建根的左子树
		++(*pindex);
		root->left = _BinaryTreeCreate(array, size, pindex, invalid);
		///递归创建根的左子树
		++(*pindex);
		root->right = _BinaryTreeCreate(array, size, pindex, invalid);
	}
	return root;
}
BTNode* BinaryTreeCreate(DataType array[], int size, char invalid)
{
	int index = 0;
	return _BinaryTreeCreate(array, size, &index, invalid);
}

//二叉树的销毁
void BinaryTreeDestroy(BTNode** proot)
{
	assert(proot);
	if (NULL == *proot)
		return;
	BinaryTreeDestroy(&(*proot)->left);//递归销毁根的左子树
	BinaryTreeDestroy(&(*proot)->right);//递归销毁根的右子树

	free(*proot); //释放根节点空间
	*proot == NULL;
}

void TestBinaryTree()
{
    int array[] = { 1, 2, 3, '#', '#', '#', 4, 5, '#', '#', 6 };
	BTNode* root = BinaryTreeCreate(array, sizeof(array) / sizeof(array[0]), '#');
}

1.5完全二叉树的判断

 完全二叉树:层从上往下,每层节点从左至右依次摆出来

既然是按照层序的方式依次将节点摆出来,因此在检测是否为完全二叉树的时候,按照层序遍历的方式来检测。

完全二叉树所有节点的情况可以分为以下两种:

找到第一个孩子不全的节点非常关键,按照层序遍历的方式找第一个不饱和节点(指该节点要么没有孩子,要么只有一个左孩子),第一个不饱和节点之后的节点不能有孩子。

 以该二叉树为例:

//判断完全二叉树
//利用二叉树的层序遍历找第一个不饱和节点的位置
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	int flag = 0;//标记第一个不饱和节点的位置
	int CompleteTree = 1;

	//空树也是完全二叉树
	if (NULL == root)
		return 1;

	QueueInit(&q);
	QueuePush(&q, root);将根节点入队列
	while (!QueueEmpty(&q))
	{
		BTNode* cur = QueueFront(&q);
		QueuePop(&q);遍历后从队列中删除该节点
		if (flag)//若找到第一个未饱和的节点
		{
			if (cur->left || cur->right)//第一个未饱和节点的后续节点不能有孩子,
			{
				CompleteTree = 0;
				break;
			}
		}
		else
		{
			//cur为饱和的节点,左右孩子都存在
			if (cur->left&&cur->right)
			{
				QueuePush(&q, cur->left);
				QueuePush(&q, cur->right);
			}
			else if (cur->left)//只有左孩子,没有右孩子,第一个不饱和的节点找到
			{
				QueuePush(&q, cur->left);
				flag = 1;
			}
			else if (cur->right)//只有右孩子,没有左孩子
			{
				QueuePush(&q, cur->right);
				CompleteTree = 0;
				break;
			}
			else
			{
				//左右孩子都不存在,则该节点为第一个不饱和的节点
				flag = 1;
			}
		}
	}
	QueueDestroy(&q);
	return CompleteTree;
}

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值