【数据结构】二叉树

关于二叉树的相关操作,是在先要构造出一个二叉树的基础之上,如何创建二叉树?树的存储结构又是怎样表示的?

树的存储结构

typedef int DataType1;
typedef struct TNode
{
	DataType1 data;
	struct TNode *lctree;
	struct TNode *rctree;
}TNode;

二叉树的创建

  • 根据带空结点的前序,采用递归的形式创建二叉树
  • 将函数外的前序数组size以传址的方式带入CreateTree函数内部,每创建一个结点都在函数内部使size-1
  • 局部视野下,每当创建完左子树,需要创建右子树时都可以通过未创建结点前的数组大小,减去创建根结点以及左子树后的数组大小
//创建结点
TNode* CreateTNode(DataType1 data)
{
	TNode *newNode = (TNode *)malloc(sizeof(TNode));
	assert(newNode);
	newNode->data = data;
	newNode->lctree = NULL;
	newNode->rctree = NULL;

	return newNode;
}
TNode *CreateTree(DataType1 *preorder, int *sz)
{
	TNode *root = NULL;
	TNode *lroot = NULL;
	TNode *rroot = NULL;
	int n = *sz;//局部视野下未创建结点前的数组大小
	DataType1 rootValue = 0;
	if (*sz == 0)
	{
		return NULL;
	}
	rootValue = preorder[0];
	(*sz)--;
	if (rootValue == -1)
	{
		return NULL;
	}
	root = CreateTNode(rootValue);
	lroot = CreateTree(preorder+1, sz);
	root->lctree = lroot;
	rroot = CreateTree(preorder+(n-(*sz)), sz);//n-(*sz)表示用去的结点个数
	root->rctree = rroot;
	return root;
}

二叉树的前/中/后序遍历

相对于左右子树的遍历顺序,根结点的遍历次序的不同形成了二叉树的不同遍历形式,根—左子树—右子树的顺序是前序遍历;左子树—根---右子树的顺序是中序遍历;左子树—右子树—根的顺序是后序遍历

void Preorder(TNode *root)
{
	
	if (root == NULL) 
	{
		return;
	}
	printf("%d ", root->data);	
	Preorder(root->lctree);	
	Preorder(root->rctree);
}

void Inorder(TNode *root)
{	
	if (root == NULL) 
	{
		return;
	}	
	Inorder(root->lctree);
	printf("%d ", root->data);	
	Inorder(root->rctree);
}

void Postorder(TNode *root)
{
	if (root == NULL) 
	{
		return;
	}	
	Postorder(root->lctree);
	Postorder(root->rctree);
	printf("%d ", root->data);	
}

求节点的个数

递推式:结点的个数=左子树节点个数+右子树节点个数+1(根节点)
二叉树有五种形式:空树、仅有一个结点、有左子树、有右子树、左右子树都存在
仅当空树时返回0,其余情况均满足递推式

int GetNodeSize(TNode *root)
{
	if (root == NULL)
	{
		return 0;
	}
	return GetNodeSize(root->lctree) + GetNodeSize(root->rctree) + 1;
}

求第K层结点个数

递推式:第k层结点个数=左子树k-1层结点个数+右子树k-1层结点个数
假设根结点为第1层,求第k层即求左右子树的第k-1层节点个数之和,如果有第k层,则不断递归,当k==1时就为第k层,此时返回1,无法递归到第k层遇到空树返回0

int LevelKNode(TNode *root, int k)
{
	int left = 0;
	int right = 0;
	if (root == NULL)
	{
		return 0;
	}
	if (k==1)
	{
		return 1;
	}
	left = LevelKNode(root->lctree, k-1);
	right = LevelKNode(root->rctree, k-1);
	return left + right;
}

叶子结点个数

递推式:叶子结点个数=左子树叶子结点个数+右子树叶子结点个数
空树时返回0;左右子树都为空树时,表示该结点为叶子结点,返回1

int GetLeafSize(TNode *root)
{
	if(root == NULL)
	{
		return 0;
	}
	if (root->lctree == NULL && root->rctree == NULL)
	{
		return 1;
	}
	return GetLeafSize(root->lctree) + GetLeafSize(root->rctree);
}

求树的高度

递推式:MAX(左子树高度, 右子树高度)+ 1
空树返回0,其余情况均满足上面递推式

#define MAX(a,b) ((a) > (b) ? (a) : (b))
int GetHeight(TNode *root)
{
	int LeftHeight = 0;
	int RightHeight = 0;
	if (root == NULL)
	{
		return 0;
	}
	LeftHeight = GetHeight(root->lctree);
	RightHeight = GetHeight(root->rctree);
	return MAX(LeftHeight, RightHeight) + 1;
}

在树中查找结点

找到了返回结点地址,找不到返回NULL
先判断根结点,如果根命中返回根结点地址,若未命中分别到左右子树里面去递归查找

TNode *Search(TNode *root, DataType1 data)
{
	TNode *node = NULL;

	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == data)
	{
		return root;
	}
	node = Search(root->lctree, data);
	if (node != NULL)
	{
		return node;
	}
	node = Search(root->rctree, data);
	if (node != NULL)
	{
		return node;
	}
	return NULL;
}

层序遍历

借助队列,根据队列先进先出的特点,先让根节点入队列,再分别让根结点的左右子树(如果不为空树)的根结点入队列,每次都取队首元素遍历,遍历完后出队列,当队列不为空时持续上面的过程,直到遍历完成

void LevelOrder(TNode *root)
{
	TNode *front = NULL;
	Queue q;
	QueueInit(&q);
	if (root == NULL)
	{
		return;
	}

	InQueue(&q, root);
	while (!QueueEmpty(&q))
	{
		front = (TNode *)QueueFront(&q);
		printf("%d ", (front->data));
		if (front->lctree != NULL)
		{
			InQueue(&q, front->lctree);
		}
		if (front->rctree != NULL)
		{
			InQueue(&q, front->rctree);
		}
		OutQueue(&q);
	}
	QueueDestroy(&q);
}

判断是否为完全二叉树

借助带空结点的层序遍历完成,如果层序遍历的过程中遇到空结点,之后仍存在不为空结点的结点,说明该树不为完全二叉树,否则就是完全二叉树

int IsCompleteBTree(TNode *root)
{
	TNode *front = NULL;
	Queue q;
	QueueInit(&q);
	if (root == NULL)
	{
		return 1;
	}

	InQueue(&q, root);
	while (!QueueEmpty(&q))
	{
		front = (TNode *)QueueFront(&q);
		if (front == NULL)
		{
			break;
		}
		//无需判断,空结点同样入队列
		InQueue(&q, front->lctree);
		InQueue(&q, front->rctree);
		OutQueue(&q);
	}
	while (!QueueEmpty(&q))
	{
		front = (TNode *)QueueFront(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return 0;
		}
		OutQueue(&q);
	}
	QueueDestroy(&q);
	return 1;
}

前/中/后序的非递归遍历

递归遍历是依靠系统调用栈完成,非递归遍历则是依靠数据结构栈完成
在后序遍历的中,要对右子树是否遍历过进行判断

void PreorderLoop(TNode *root)
{
	TNode *node = NULL;
	Stack stack;
	StackInit(&stack);

	node = root;
	while (node != NULL || !StackEmpty(&stack))
	{
		while (node != NULL)
		{
			printf("%d ", node->data);
			StackPush(&stack, node);
			node = node->lctree;
		}
		node = ((TNode *)StackTop(&stack))->rctree;
		StackPop(&stack);
	}
	StackDestroy(&stack);
}

void InorderLoop(TNode *root)
{
	TNode *node = NULL;
	Stack stack;
	StackInit(&stack);

	node = root;
	while (node != NULL || !StackEmpty(&stack))
	{
		while (node != NULL)
		{
			StackPush(&stack, node);
			node = node->lctree;
		}
		printf("%d ", ((TNode *)StackTop(&stack))->data);
		node = ((TNode *)StackTop(&stack))->rctree;
		StackPop(&stack);
	}
	StackDestroy(&stack);
}

void PostorderLoop(TNode *root)
{
	TNode *node = NULL;
	TNode *top = NULL;
	TNode *last = NULL;
	Stack stack;
	StackInit(&stack);

	node = root;
	while (node != NULL || !StackEmpty(&stack))
	{
		while (node != NULL)
		{
			StackPush(&stack, node);
			node = node->lctree;
		}

		top = (TNode *)StackTop(&stack);
		if (top->rctree == NULL || top->rctree == last)//top->rctree == last时表示右子树已遍历
		{
			printf("%d ", top->data);
			last = top;
			StackPop(&stack);
		}
		else
		{
			node = top->rctree;
		}
	}
	StackDestroy(&stack);
}

镜像二叉树

当为空树或者为叶子结点时直接返回,其余情况均交换左右子树,整个过程递归实现

void Mirror(TNode *root)
{
	TNode *tmp = NULL;
	if (root == NULL)
	{
		return;
	}
	if (root->lctree != NULL && root->rctree != NULL)
	{
		tmp = root->lctree;
		root->lctree = root->rctree;
		root->rctree = tmp;
		Mirror(root->lctree);
		Mirror(root->rctree);
	}
}

找两结点的最近祖先结点

整个过程递归完成,如果两结点分别在根节点的左右子树中,则说明此根结点为最近的祖先结点,如果两个结点均在同一子树中,则递归进入该子树进行查找

TNode * GetNearestAncestor(TNode *root, DataType1 d1, DataType1 d2)
{
	TNode *d1InLeft = Search(root->lctree, d1);
	TNode *d2InLeft = Search(root->lctree, d2);

	if (d1InLeft && !d2InLeft) {
		return root;
	}

	if (!d1InLeft && d2InLeft) {
		return root;
	}

	if (d1InLeft && d2InLeft) {
		return GetNearestAncestor(root->lctree, d1, d2);
	}
	
	return GetNearestAncestor(root->rctree, d1, d2);
}

判断是否为平衡二叉树

什么样的树是平衡二叉树?

  • 空树是平衡二叉树
  • 平衡二叉树的左右子树也为平衡二叉树
  • 平衡二叉树的左右子树高度差不超过1

先递归判断左右子树是否为平衡二叉树,如果都是再判断左右子树的高度差

int IsBalance(TNode *root)
{
	int leftHeight, rightHeight, diff, isBalance;
	if (root == NULL) {
		return 1;
	}

	isBalance = IsBalance(root->lctree);
	if (isBalance == 0) {
		return 0;
	}

	isBalance = IsBalance(root->rctree);
	if (isBalance == 0) {
		return 0;
	}

	leftHeight = GetHeight(root->lctree);
	rightHeight = GetHeight(root->rctree);
	diff = leftHeight - rightHeight;
	if (diff < -1 || diff > 1) {
		return 0;
	}
	else {
		return 1;
	}
}

求树最远的结点距离

空树的最远结点距离为0,剩下的可分为两种情况:最远距离=左子树高度+右子树高度,另一种情况即为最远距离诞生于左/右子树,此时递归求取该子树的最远距离,然后取其较大者即为最终要求的最远距离

int Max(int a, int b, int c)
{
	int max = 0;
	if (a >= b) 
	{
		max = a;
	}

	if (c >= max) 
	{
		max = c;
	}

	return max;
}

int GetFarrestDistance(TNode *root)
{
	int leftDistance, rightDistance, leftHeight, rightHeight, rootDistance;
	if (root == NULL) 
	{
		return 0;
	}

	leftDistance = GetFarrestDistance(root->lctree);
	rightDistance = GetFarrestDistance(root->rctree);

	leftHeight = GetHeight(root->lctree);
	rightHeight = GetHeight(root->rctree);
	rootDistance = leftHeight + rightHeight;

	return Max(leftDistance, rightDistance, rootDistance);
}

根据前序和中序创建二叉树

根据前序和中序能够确定二叉树的根节点、左子树、右子树三部分,然后递归的创建每一个子树

TNode * CreateTree2(DataType1 preorder[], DataType1 inorder[], int size)
{
	int i, r;
	DataType1 rootValue = preorder[0];
	TNode *root = NULL;
	if (size <= 0) 
	{
		return NULL;
	}

	for (i = 0; i < size; i++) 
	{
		if (inorder[i] == rootValue) 
		{
			r = i; 
			break;
		}
	}
	assert(r < size);
	root = CreateTNode(rootValue);
	root->lctree = CreateTree2(preorder + 1, inorder, r);
	root->rctree = CreateTree2(preorder + 1 + r, inorder + r + 1, size - 1 - r);

	return root;
}

同样根据后序和中序也能够创建二叉树

TNode * CreateTree3(DataType1 inorder[], DataType1 postorder[], int size)
{
	int i, r;
	DataType1 rootValue = postorder[size - 1];
	TNode *root = NULL;
	if (size <= 0) 
	{
		return NULL;
	}

	for (i = 0; i < size; i++) 
	{
		if (inorder[i] == rootValue) 
		{
			r = i; 
			break;
		}
	}
	assert(r < size);
	root = CreateTNode(rootValue);
	root->lctree = CreateTree3(inorder, postorder, r);
	root->rctree = CreateTree3(inorder + 1 + r, postorder + r, size - 1 - r);

	return root;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值