【无标题】 二叉树操作详解

二叉树块

我们先预想建立一个二叉树(如图),首先对其进行前中后三种遍历
在这里插入图片描述

前序遍历

按根左右的顺序一层一层遍历
A	  	B					C
A	  	B	D   E		C F G
A	  	B	D   E H	C F G
代码如下

void BinaryTreePrevOrder(BTNode* root)
{
	if (root==NULL)
	{
		return;
	}
	printf("%c ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}

前序遍历是每到一个节点,先输出该节点数据,再往左递归,待左边指针为空,再往回递归

中序遍历

和前序遍历操作差不多,逻辑上按左根右遍历
B			A			C
D  B	E		A		F	C  G
D  B	E	H	A		F	C  G

void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeInOrder(root->_left);
	printf("%c ", root->_data);
	BinaryTreeInOrder(root->_right);
}

中序遍历是每到一个节点,优先往左递归,待递归到空指针,再回来输出节点数据

后序遍历

逻辑上按左右根遍历
B	  		C		A	
D      E  B F G	C		A	
D  H  E  B F G	C		A

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreePostOrder(root->_left);
	BinaryTreePostOrder(root->_right);
	printf("%c ", root->_data);
}

构建二叉树

从数组中读取数据构建二叉树,选定终止条件,即读取的数据下标大于等于最大下标n,这边选用pi而不是pi,是因为不传pi的话,往下递归会重新产生名为pi的形参,而原来的pi并没有因为形参的改变而改变
在这里插入图片描述

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
//a为数组,n为数组最大下标,*pi表示当前读取位置下标
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	if ((*pi) >= n)
	{
		return NULL;
	}
	if (a[(*pi)] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return;
	}
	node->_data = a[(*pi)++];
	node->_left = BinaryTreeCreate(a, n, pi);
	node->_right = BinaryTreeCreate(a, n, pi);
	return node;
}

思路:每读取一个字符就申请一个结构体储存,并且按照前序遍历顺序储存

二叉树销毁

这里因为root是指针,要把它置空,就要用二级指针

void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
	BinaryTreeDestory(&((*root)->_left));
	BinaryTreeDestory(&((*root)->_right));
	free(*root);
	*root = NULL;
}

二叉树节点个数

int BinaryTreeSize(BTNode* root)
{
	if (root== NULL)
		return 0;
	return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

返回的是左边节点加右边节点加该节点自身,故而+1。
推荐大家多画递归展开图理解
在这里插入图片描述

二叉树叶子节点个数

//是叶子节点就返回1,不是就接着往左往右递归
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层节点个数

首先要判断有没有k层,且k是不是大于0,然后往下递归。每递归一层,k-1,一直到k==1,如果还不为空,就表示有一个这样的节点,把这样的节点数量加起来,就得到个数了。这里不能传int* k,操作要求是每下一层减1,不是每递归一次减1。

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left, k-1)+BinaryTreeLevelKSize(root->_right, k-1);
}

二叉树查找值为x的节点

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->_data == x)
		return root;
	BTNode* ret1=BinaryTreeFind(root->_left, x);
	if (ret1)
		return ret1;
	BTNode* ret2 = BinaryTreeFind(root->_right, x);
	if (ret2)
		return ret2;
	return NULL;//找不到返回空指针
}

这边注意找到了是一层一层返回,难理解建议画展开图

层序遍历

这里以及判断二叉树是否是完全二叉树需要用到队列的知识,队列代码放下面了。
思路为先把根节点入队列,然后取队头用front接收,输出数据并出队列,再把左右节点(非空)入队,然后出左节点,再把左节点的左右节点入队,接着又是右节点,依此循环,直到队列为空。

void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c ", front->_data);
		QueuePop(&q);
		if (front->_left)
			QueuePush(&q, front->_left);
		if (front->_right)
			QueuePush(&q, front->_right);
	}
	printf("\n");
	QueueDestroy(&q);
}

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

这里和层序遍历差不多,区别是空指针也入队列,以及在遍历到空指针出队列就结束循环。如果是完全二叉树,自第一个空指针出队列,后面就全是空指针了。建议自己画队列理解

int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueuePush(&q, front->_left);
			QueuePush(&q, front->_right);
		}
		else
			break;
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return 0;
		}
	}
	QueueDestroy(&q);
	return 1;
}

队列块

我设置的队列是带头节点的单链表,队头指针指向头节点。然后我队列的操作是放在二叉树头文件里的,我觉得方便一点,不用考虑头文件包含问题,也不会出现复制路径操作不当出现头文件打不开的问题
在这里插入图片描述

不多赘述了,代码如下

初始化队列

void QueueInit(Queue* q)
{
	assert(q);
	QNode* p = (QNode*)malloc(sizeof(QNode));
	if (p == NULL)
	{
		perror("malloc fail");
		return;
	}
	p->_next = NULL;
	q->_front = p;
	q->_rear = p;
}

队尾入队列

void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* p = (QNode*)malloc(sizeof(QNode));
	if (p == NULL)
	{
		perror("malloc fail");
		return;
	}
	p->_next = NULL;
	p->_data = data;
	q->_rear->_next = p;
	q->_rear = p;
}

队头出队列

void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	if (q->_front->_next == q->_rear)
	{
		free(q->_rear);
		q->_rear = q->_front;
	}
	else
	{
		QNode* cur = q->_front->_next;
		q->_front->_next = cur->_next;
		free(cur);
		cur = NULL;
	}
}

获取队列头部元素

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->_front != q->_rear);
	return q->_front->_next->_data;
}

检测队列是否为空

如果为空返回非零结果,如果非空返回0

int QueueEmpty(Queue* q)
{
	assert(q);
	return q->_front == q->_rear;
}

销毁队列

void QueueDestroy(Queue* q)
{
	assert(q);
	while(!QueueEmpty(q))
	    QueuePop(q);
	free(q->_front);
	q->_front = NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值