数据结构(二叉树的链式结构)

链式结构的二叉树

⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。

通常的⽅法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩⼦所在的链结点的存储地址, 其结构如下:

typedef int BTDataType;
// ⼆叉链
typedef struct BinaryTreeNode
{
 struct BinTreeNode* left; // 指向当前结点左孩⼦ 
 struct BinTreeNode* right; // 指向当前结点右孩⼦ 
 BTDataType val; // 当前结点值域 
}BTNode;

⼆叉树的创建⽅式

⼆叉树的创建⽅式⽐较复杂,其运用了大量的递归知识。为了更好的步⼊到⼆叉树内容中,我们先⼿动创建⼀棵链式⼆叉树:

BTNode* buyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->left = newnode->right = NULL;

	return newnode;
}

BTNode* CreateTree()
{
 BTNode* n1 = BuyBTNode(1);
 BTNode* n2 = BuyBTNode(2);
 BTNode* n3 = BuyBTNode(3);
 BTNode* n4 = BuyBTNode(4);
 BTNode* n5 = BuyBTNode(5);
 BTNode* n6 = BuyBTNode(6);
 BTNode* n7 = BuyBTNode(7);
 n1->left = n2;
 n1->right = n4;
 n2->left = n3;
 n4->left = n5;
 n4->right = n6;
 n5->left = n7;
 return n1;
}

我们再来回顾一下⼆叉树的概念:

⼆叉树分为空树和⾮空⼆叉树,⾮空⼆叉树由根结点、根结点的左⼦树、根结点 的右⼦树组成的

根结点的左⼦树和右⼦树分别⼜是由⼦树结点、⼦树结点的左⼦树、⼦树结点的右⼦树组成的,因此,⼆叉树定义是递归式的,后序链式⼆叉树的操作中基本都是按照该概念实现的。


前中后序遍历

⼆叉树的操作离不开树的遍历,下面我们先来看看⼆叉树的遍历有哪些⽅式:

遍历规则:

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

1)前序遍历(PreorderTraversal亦称先序遍历):访问根结点的操作发⽣在遍历其左右⼦树之前

        访问顺序为:根结点、左⼦树、右⼦树

2)中序遍历(InorderTraversal):访问根结点的操作发⽣在遍历其左右⼦树之中(间)

        访问顺序为:左⼦树、根结点、右⼦树

3)后序遍历(PostorderTraversal):访问根结点的操作发⽣在遍历其左右⼦树之后

        访问顺序为:左⼦树、右⼦树、根结点

前序遍历

void PreOrder(BTNode* root)
{
 if (root == NULL)
 {
 printf("N ");
 return;
 }
 printf("%d ", root->val);
 PreOrder(root->left);
 PreOrder(root->right);
}

中序遍历

void InOrder(BTNode* root)
{
 if (root == NULL)
 {
 printf("N ");
 return;
 }
 InOrder(root->left);
 printf("%d ", root->val);
 InOrder(root->right);
}

后序遍历

void PostOrder(BTNode* root)
{
 if (root == NULL)
 {
 printf("N ");
 return;
 }
 InOrder(root->left);
 InOrder(root->right);
 printf("%d ", root->val);
}

图解遍历:

以前序遍历为例子:

函数递归的栈帧图:

前序遍历结果:1 2 3 4 5 6

中序遍历结果:3 2 1 5 4 6

后序遍历结果:3 1 5 6 4 1

结点的个数及其高度

// ⼆叉树结点个数 
int BinaryTreeSize(BTNode* root); 
// ⼆叉树叶⼦结点个数 
int BinaryTreeLeafSize(BTNode* root); 
// ⼆叉树第k层结点个数 
int BinaryTreeLevelKSize(BTNode* root, int k); 
//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root);
// ⼆叉树查找值为x的结点 
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root);

以下是其的实现方式:

// ⼆叉树结点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
// ⼆叉树叶⼦结点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

// ⼆叉树第k层结点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) 
		+ BinaryTreeLevelKSize(root->right, k - 1);
}
//⼆叉树的深度/⾼度
int BinaryTreeDepth(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int leftDep = BinaryTreeDepth(root->left);
	int rightDep = BinaryTreeDepth(root->right);

	return leftDep > rightDep ? leftDep + 1 : rightDep + 1;
}
// ⼆叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}

	BTNode* leftFind =  BinaryTreeFind(root->left, x);
	if (leftFind)
	{
		return leftFind;
	}
	BTNode* rightFind =  BinaryTreeFind(root->right, x);
	if (rightFind)
	{
		return rightFind;
	}
	return NULL;
}

// ⼆叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->left));
	BinaryTreeDestory(&((*root)->right));

	free(*root);
	*root = NULL;
}

层序遍历

设⼆叉树的根结点所在层数为1,层序遍历就是从所在⼆叉树的根结点出发,⾸先访问第⼀层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,⾃上⽽下,⾃左⾄右逐层访问树的结点的过程就是层序遍历

实现层序遍历需要额外借助数据结构:队列

下图为其实现的代码:

//层序遍历
//借助数据结构---队列
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		//取队头,打印
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		//队头节点的左右孩子入队列
		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
	//队列为空
	QueueDestroy(&q);
}


判断是否为完全二叉树

//判断二叉树是否为完全二叉树
//---队列
bool BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	//队列不一定为空
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

结尾

以上便是本期的全部内容,感谢各位的观看与支持!

猜你喜欢

转载自blog.csdn.net/2406_83947720/article/details/140756437