【算法与数据结构】深入二叉树实现超详解(全源码优化)

这篇具有很好参考价值的文章主要介绍了【算法与数据结构】深入二叉树实现超详解(全源码优化)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言


📝前言

上节我们学习了二叉树(前中后)序遍历
这节将实现二叉树。

让我们复习一下二叉树,接着就是二叉树的实现了😊,学习起来吧!

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
    【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言
    本节代码举例图
    【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言
    好了,差不多了,启动!—》

🌠 接口函数

头文件Tree.h,这里封装了树的接口,需要时直接#include"Tree.h"。

# define _CRT_SECURE_NO_WARNINGS 1

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

//通过前序遍历的数组“ABD##E#H##CF##G##”构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
//二叉树销毁
BTNode* BinaryTreeDestory(BTNode** root);
//二叉树节点个数
int BTNodeTreeSize(BTNode* root);
//二叉树叶子节点个数
int BTNodeTreeLeafSize(BTNode* root);
//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
//二叉树前序遍历
void BinaryTreePrevOreder(BTNode* root);
//二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
//二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
//层序遍历
void BinaryTreeLevelOrder(BTNode* root);
//求树的高度
int BinaryHeight(BTNode* root);
//判断二叉树是否是完全二叉树
bool BinaryTreeCompelete(BTNode* root);

//前序遍历
void BTreePrever(BTNode* root);
//中序遍历
void BTreeQrder(BTNode* root);
//后序遍历
void BTreeBack(BTNode* root);

✏️ 实现函数

🌉创建树的新节点

//创建新节点
BTNode* BuyBTNode(int val)
{
	//使用malloc动态申请内存,分配一个BTNode类型的结构体。
	BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
	if (newNode == NULL)//
	{
		perror("malloc fail");//检查malloc是否成功
		return ;
	}
	newNode->data = val;//将新节点的data字段设置为传入的val值。
	newNode->left = NULL;//将新节点的left和right子节点指针设置为NULL。
	newNode->right = NULL;
	return newNode;//返回新创建的节点指针。
}

动态创建一个新的二叉树节点,并初始化其data和子节点指针字段。这样就可以在构建二叉树的过程中不断调用这个函数来创建新的节点,然后连接起来形成树形结构。

🌠通过前序遍历的数组构建二叉树

BTNode* BinaryTreeCreateHelper(BTDataType* a, int n, int* pi)
{									//n是数组a的长度
									//pi是索引指针,用于遍历a数组
	if (*pi >= n || a[*pi] == 'N')//检查*pi是否越界或当前元素为'N',如果是则跳过该节点,					(*pi)++向后移动。这里的N意思是空
	{
		(*pi)++;//
		return NULL;
	}
	//否则,调用BuyBTNode函数创建新的节点,并将a[*pi]的值赋给节点。(*pi)++后移。
	BTNode* newNode = BuyBTNode(a[(*pi)++]);
	if (newNode != NULL)
	{
		newNode->left = BinaryTreeCreateHelper(a, n, pi);//递归创建左子节点
		newNode->right = BinaryTreeCreateHelper(a, n, pi);//递归创建右子节点
	}

	return newNode;//每次递归都返回创建好的节点。

}

通过递归和索引下标的递增,就可以按先序遍历顺序依次创建二叉树的每个节点,并建立节点之间的父子关系,最终返回根节点,就完成了整个二叉树的创建。利用了递归的思想,通过不断调用自身函数来实现结构的生成。

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	return BinaryTreeCreateHelper(a, n, pi);
}

🌉包装通过前序遍历的数组构建二叉树

BinaryTreeCreate函数是对BinaryTreeCreateHelper的一个包装。BinaryTreeCreateHelper负责具体创建二叉树的递归操作,BinaryTreeCreate作为入口函数,接收参数,调用辅助函数创建树,并返回根节点指针。

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	return BinaryTreeCreateHelper(a, n, pi);
}

这样设计有利于函数单一职责原则,明确划分主函数和辅助函数的职责,更好地实现二叉树从先序序列的创建。

这里的三种方法构建,其实是我们通过前面我们将用前序构建出来一个二叉树,这个三种呢,其实是基于前面一盹乱来,将前面的已经构建好的,遍历打印刷一遍出来!哈哈哈,以下是代码遍历来刷一遍:

🌉前序构建二叉树
void BTreePrever(BTNode* root)
{
	if (root == NULL)
		return;
	printf("%c ", root->_data);
	BTreeQrder(root->_left);
	BTreeQrder(root->_right);

}
🌉中序构建二叉树
void BTreeQrder(BTNode* root)
{
	if (root == NULL)
		return;
	BTreeQrder(root->_left);
	printf("%c ", root->_data);
	BTreeQrder(root->_right);

}
🌉后序构建二叉树
void BTreeBack(BTNode* root)
{
	if (root == NULL)
		return;
	BTreeQrder(root->_left);
	BTreeQrder(root->_right);
	printf("%c ", root->_data);

}

🌠二叉树的销毁

BinaryTreeDestory函数是用于释放二叉树占用的内存的。

void BinaryTreeDestory(BTNode** root)
{
	if (*root != NULL)
	{
		BinaryTreeDestory(&((*root)->left)); //释放左子树
		BinaryTreeDestory(&((*root)->right)); //释放右子树
		free(*root);//
		*root = NULL;
	}
}

🌠层次遍历

什么是层次遍历呢?
【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言
什么是层次遍历呢?我们先了解下层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

So->

因此我们可以用队列控制,出队入队遍历二叉树

🌠第一种实现:不用数组模拟队列

使用队列queue来模拟层序遍历,front和rear指针分别表示队头和队尾,front指针控制出队,rear指针控制入队,两个指针同时移动,就可以模拟出层序的遍历顺序。

void LevelOrder(BTNode* root) 
{
	if (root == NULL)//首先判断如果根节点为空,直接返回
		return;

	BTNode* queue[1000]; //使用队列queue来模拟层序遍历,front和rear指针分别表示队头和队尾
	int front = 0, rear = 0;
	queue[rear++] = root;//根节点入队

	while (front < rear) //循环结束时,front指针一定会等于rear指针,队列为空,遍历结束
	{											//关键是front指针每次递增1,实现队首出队
		BTNode* current = queue[front++];//取出队首节点current
		printf("%c ", current->data); 
		if (current->left != NULL)//如果当前节点有左子节点,将左子节点加入队尾
			queue[rear++] = current->left;
		if (current->right != NULL)//如果当前节点有右子节点,将右子节点加入队尾
			queue[rear++] = current->right;//rear指针每次递增1,实现节点入队
	}
}

【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言
【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言

🌠第二种实现:不用数组模拟队列,创建队列实现

特别注意当我们在这里的取头元素记得要要用数的结构体类型,否则就会崩溃找很久错误,对当然,也可以换取队列的头文件的类型换成BTNode*,而不是int和其他类型


void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);//初始化队列
	if (root)
		QueuePush(&q, root);//入队
	while (!QueueEmpty(&q))//判断是否为空
	{
		//特别注意当我们在这里的取头元素记得要要用数的结构体类型,否则就会崩溃找很久错误,对当然,也可以换取队列的头文件的类型换成BTNode*,当然文章后面有完整代码,经过优化的,这个是阿森太笨错的,记录下哈哈哈
		BTNode* front = QueueFront(&q);//取队头
		QueuePop(&q);//出队

		if (front)
		{
			printf("%d ", front->data);

			//带入下一层
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
		else
		{
			printf("N ");
		}
	}
	printf("\n");

	QueueDestroy(&q);//销毁队列
}

【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言

while循环条件是判断队列是否为空,不是front和rear指针比较。每次从队列取出节点front后,直接打印数据,然后入队其子节点。如果front为空,打印一个标识符"N"。

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

【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言

  1. 数组模拟队列实现:
//辅助函数:判断队列是否为空
int QueueIsEmpty(int front, int rear)
{
	return front == rear;
}

//判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	if (root == NULL)
		return 1; //空树是完全二叉树

	BTNode* queue[1000];//用于存放节点的队列
	int front = 0, rear = 0;
	int flag = 0;//用于标记是否遇到空节点

	//根节点入队
	queue[rear++] = root;

	while (!QueueIsEmpty(front, rear))
	{
		BTNode* current = queue[front++];

		//如果遇到空节点后面还有非空节点,说明不是完全二叉树
		if (current == NULL)
			flag = 1;
		else
		{
			//左右孩子入队
			queue[rear++] = current->left;
			queue[rear++] = current->right;
			//如果遇到空节点后面还有非空节点,说明不是完全二叉树
			if (flag)
				return 0;
		}
	}

	//遍历结束,说明是完全二叉树
	return 1;
}

首先使用队列来层序遍历二叉树根节点入队,循环取出队首节点current,如果current为空,设置flag标记为1,表示遇到了空节点,如果current非空,将其左右孩子入队,如果flag已经被设置为1,说明之前遇到了空节点,现在又有非空节点,必然不是完全二叉树,直接返回0,遍历结束,说明没有发现flag为1后还有非空节点的情况,即树节点是从左到右依次完整填充每一层的,就是完全二叉树,返回1

  1. 用队列实现:
//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue queue;
	QueueInit(&queue);
	if (root)
		QueuePush(&queue, root);
	while (!QueueEmpty(&queue))
	{
		QDataType front = QueueFront(&queue);
		QueuePop(&queue);
		if (front == NULL)
			break;
		QueuePush(&queue, front->_left);
		QueuePush(&queue, front->_right);
	}

	//break跳出后
	//判断这后的元素是否有非空值
	//有非控制则不是完全二叉树

	while (!QueueEmpty(&queue))
	{
		QDataType front = QueueFront(&queue);
		QueuePop(&queue);
		if (front != NULL)
		{
			QueueDestroy(&queue);
			return false;
		}
	}
	//如果没有非空值返回true
	QueueDestroy(&queue);
	return true;
}

这里的逻辑没什么大变化,当然也有一些小细节:
这是队列的销毁,和平常一样:

void QueueDestroy(Queue* pq)
{

	assert(pq); // 断言队列指针是否为空
	QNode* cur = pq->phead; // cur指向队列头节点
	while (cur)
	{					//切莫将cur->next写成pq->phead->next
		QNode* next = cur->next; // 保存cur下一个节点的指针
		free(cur); // 释放cur节点内存
		cur = next; // cur指针移到下一个节点

	}
	pq->phead = pq->ptail = NULL; // 重置头尾节点指针为空
	pq->size = 0; // 重置队列大小为0

}

🌠二叉树节点个数

【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言

//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;//递归终止条件是空节点,返回0
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
	//非空节点返回它本身+左子树+右子树节点个数的和
}

这个函数BinaryTreeSize用来计算一棵二叉树的节点总个数。,如果传入的根节点root为空,说明这棵二叉树没有节点,返回0,如果不是空节点,则:这个节点本身就是1个节点,加上它左子树的节点个数BinaryTreeSize(root->left),加上它右子树的节点个数BinaryTreeSize(root->right),递归计算左右子树节点个数,然后汇总返回。时间复杂度为O(N),只需要一次遍历二叉树。

🌉二叉树叶子节点个数

//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;//如果root节点的左右子节点都为空,则root就是叶子节点,返回1
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

如果传入的根节点root为空,说明这棵二叉树没有节点,返回0如果root节点的左右子节点都为空,则root就是叶子节点,返回1,否则:计算root的左子树叶子节点个数BinaryTreeLeafSize(root->left),计算root的右子树叶子节点个数BinaryTreeLeafSize(root->right)
汇总返回左右子树叶子节点个数之和

🌉二叉树第k层节点个数

//二叉树滴k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k == 1)
		return 1;
		//计算root的左子树第k-1层节点个数          //计算root的右子树第k-1层节点个数
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

递归基线是查询第一层或空树时返回值,每次递归都将层数k减1,向下查询下一层,从下至上不断累加各层节点个数,时间复杂度为O(N),只需要一次遍历。利用层序遍历思想实现对指定层的统计。

🌠二叉树查找值为x的节点

//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)//根节点root为空,直接返回NULL
		return NULL;
	if (root->data == x)//root节点的数据等于查找值x,返回root
		return root;
	BTNode* leftResult = BinaryTreeFind(root->left, x);//在root的左子树中查找
	if (leftResult != NULL)//如果左子树找到返回结果,直接返回
		return leftResult;
	return BinaryTreeFind(root->right, x);//如果左子树没有找到,继续在root的右子树
}

递归终止条件是找到节点或者子树为空,先在左子树查找,如果找到直接返回,如果左子树没有找到,再在右子树查找,采用深度优先搜索的递归方式遍历整棵二叉树,时间复杂度为O(N),最坏情况需要遍历整棵二叉树。利用递归实现二叉树的深度优先搜索。

当然你也可以查找–>

// 查找x所在的节点
BTNode* TreeFind(BTNode* root, int x)
{
	if (root == NULL)//根节点root为空,直接返回NULL
		return NULL;

	if (root->val == x)//root节点的值等于x,返回root节点
		return root;

	BTNode* ret1 = TreeFind(root->left, x);
	//在root的左子树中查找TreeFind(root->left, x)
	if (ret1)//如果左子树找到节点,直接返回
		return ret1;
		
	//如果左子树没有找到,在root的右子树中查找TreeFind(root->right, x)/
	BTNode* ret2 = TreeFind(root->right, x);
	if (ret2)
		return ret2;

	return NULL;如果左右子树均没有找到,返回NULL
}

用深度优先搜索的递归方式遍历二叉树先在左子树查找,找到则返回,否则查右子树,递归终止条件是找到节点或者子树为空,时间复杂度为O(N),最坏情况需要遍历整棵树。

🌉 完整代码实现

🌉 BinaryTree.h文件
# define _CRT_SECURE_NO_WARNINGS 1

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

//通过前序遍历的数组“ABD##E#H##CF##G##”构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
//二叉树销毁
BTNode* BinaryTreeDestory(BTNode** root);
//二叉树节点个数
int BTNodeTreeSize(BTNode* root);
//二叉树叶子节点个数
int BTNodeTreeLeafSize(BTNode* root);
//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
//二叉树前序遍历
void BinaryTreePrevOreder(BTNode* root);
//二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
//二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
//层序遍历
void BinaryTreeLevelOrder(BTNode* root);
//求树的高度
int BinaryHeight(BTNode* root);
//判断二叉树是否是完全二叉树
bool BinaryTreeCompelete(BTNode* root);

//前序遍历
void BTreePrever(BTNode* root);
//中序遍历
void BTreeQrder(BTNode* root);
//后序遍历
void BTreeBack(BTNode* root);
🌉 Queue.h文件
# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include "BinaryTree.h"
typedef BTNode* QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;


//二级指针
入队列
//void QueuePush(QNode** pphead, QNode** pptail);
出队列
//void QueuePop(QNode** pphead, QNode** pptail);

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);

void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);

QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
🌉 Queue.c文件
#include "Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq); // 断言队列指针是否为空
	pq->phead = NULL; // 初始化头节点指针为空
	pq->ptail = NULL; // 初始化尾节点指针为空  
	pq->size = 0; // 初始化队列大小为0
}

void QueueDestroy(Queue* pq)
{

	assert(pq); // 断言队列指针是否为空
	QNode* cur = pq->phead; // cur指向队列头节点
	while (cur)
	{
		QNode* next = cur->next; // 保存cur下一个节点的指针
		free(cur); // 释放cur节点内存
		cur = next; // cur指针移到下一个节点

	}
	pq->phead = pq->ptail = NULL; // 重置头尾节点指针为空
	pq->size = 0; // 重置队列大小为0

}



void QueuePush(Queue* pq, QDataType* x)
{
	assert(pq); // 断言队列指针是否为空
	QNode* newnode = (QNode*)malloc(sizeof(QNode)); // 申请新节点内存
	if (newnode == NULL)
	{ // 申请失败
		perror("malloc fail");
		return;
	}
	newnode->data = x; // 设置新节点数据
	newnode->next = NULL; // 设置新节点next指针为空

	if (pq->ptail)
	{ // 如果队列不为空
		pq->ptail->next = newnode; // 尾节点指向新节点
		pq->ptail = newnode; // 更新尾节点指针
	}
	else
	{ // 队列为空
		pq->phead = pq->ptail = newnode; // 头尾节点同时指向新节点
	}

	pq->size++; // 队列大小增加1

}

//出队列
void QueuePop(Queue* pq)
{

	assert(pq); // 断言队列指针是否为空
	if (pq->phead == NULL)
		return; // 队列为空直接返回

	assert(pq->phead != NULL); // 断言头节点不为空
	if (pq->phead->next == NULL)
	{ // 队列只有一个节点
		free(pq->phead); // 释放头节点
		pq->phead = pq->ptail = NULL; // 头尾节点同时置空
	}
	else
	{ // 队列有多个节点
		QNode* next = pq->phead->next; // 保存头节点的下一个节点
		free(pq->phead); // 释放头节点
		pq->phead = next; // 头节点指向下一个节点
	}

	pq->size--; // 队列大小减1

}

QDataType QueueFront(Queue* pq)
{
	assert(pq);

	assert(pq->phead != NULL);

	return pq->phead->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);

	//暴力检查
	assert(pq->ptail != NULL);

	return pq->ptail->data;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	
	return pq->size == 0;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}
🌉 BinaryTreeTest.c文件
# define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
BTNode* BuyBTNode(int val)
{
	BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
	if (newNode != NULL)
	{
		newNode->_data = val;
		newNode->_left = NULL;
		newNode->_right = NULL;
	}

	return newNode;
}

//通过前序遍历的数组构建二叉树
BTNode* PreBinaryTreeCreateHelper(BTDataType* a, int n, int* pi)
{
	if (*pi >= n || a[*pi] == 'N')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* newNode = BuyBTNode(a[(*pi)++]);
	if (newNode != NULL)
	{
		newNode->_left = PreBinaryTreeCreateHelper(a, n, pi);
		newNode->_right = PreBinaryTreeCreateHelper(a, n, pi);
	}

	return newNode;
}

//辅助函数构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	return PreBinaryTreeCreateHelper(a, n, pi);
}

//通过前序遍历的数组打印
void BTreePrever(BTNode* root)
{
	if (root == NULL)
		return;
	printf("%c ", root->_data);
	BTreeQrder(root->_left);
	BTreeQrder(root->_right);

}

//通过中序遍历的数组构建二叉树
void BTreeQrder(BTNode* root)
{
	if (root == NULL)
		return;
	BTreeQrder(root->_left);
	printf("%c ", root->_data);
	BTreeQrder(root->_right);

}

//通过后序遍历的数组构建二叉树
void BTreeBack(BTNode* root)
{
	if (root == NULL)
		return;
	BTreeQrder(root->_left);
	BTreeQrder(root->_right);
	printf("%c ", root->_data);

}

//二叉树的销毁
BTNode* BinaryTreeDestory(BTNode** root)
{
	if (*root != NULL)
	{
		BinaryTreeDestory(&(*root)->_left);
		BinaryTreeDestory(&(*root)->_right);
		free(*root);
		*root = NULL;
	}
}



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

	//return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 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层节点个数
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);

}

//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;
	if (root->_data == x)
		return root;
	BTNode* leftResult = BinaryTreeFind(root->_left, x);
	if (leftResult != NULL)
		return leftResult;

	return BinaryTreeFind(root->_right, x);
	
	//也可以这种方法
	//if (root == NULL)
	//	return NULL;
	//if (root->_data == x)
	//{
	//	return root;
	//}
	//BTNode* res1 = BinaryTreeFind(root->_left, x);
	//if (res1)
	//	return res1;
	//BTNode* res2 = BinaryTreeFind(root->_right, x);
	//if (res2)
	//	return res2;
	//retrun NULL;
}

//层序遍历
void LevelOrder(BTNode* root)
{
	if (root == NULL)
		return;
	Queue queue;
	QueueInit(&queue);
	if (root)
		QueuePush(&queue, root);

	while (!QueueEmpty(&queue))
	{
		QDataType front = QueueFront(&queue);
		printf("%c ", front->_data);
		QueuePop(&queue);
		
		if (front->_left)
			QueuePush(&queue, front->_left);
		if (front->_right)
			QueuePush(&queue, front->_right);
	}

	QueueDestroy(&queue);
}

//求树的高度
int BinaryHeight(BTNode* root)
{
	if (root == NULL)
		return 0;
	int leftHeight = BinaryHeight(root->_left);
	int rightHeight = BinaryHeight(root->_right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
	Queue queue;
	QueueInit(&queue);
	if (root)
		QueuePush(&queue, root);
	while (!QueueEmpty(&queue))
	{
		QDataType front = QueueFront(&queue);
		QueuePop(&queue);
		if (front == NULL)
			break;
		QueuePush(&queue, front->_left);
		QueuePush(&queue, front->_right);
	}

	//break跳出后
	//判断这后的元素是否有非空值
	//有非控制则不是完全二叉树

	while (!QueueEmpty(&queue))
	{
		QDataType front = QueueFront(&queue);
		QueuePop(&queue);
		if (front != NULL)
		{
			QueueDestroy(&queue);
			return false;
		}
	}
	//如果没有非空值返回true
	QueueDestroy(&queue);
	return true;
}

🌉测试一下效果(BinaryTreeTest.c)文件

int main() 
{
    BTDataType preOrder[] = { '1','2','3','N','N','N','4','5','N','N','6','N','N' };
    int index = 0;
    BTNode* root = BinaryTreeCreate(preOrder, sizeof(preOrder) / sizeof(preOrder[0]), &index);
    //前序遍历
    printf("前序遍历:\n");
    BTreePrever(root);
    printf("\n");


    //中序遍历
    printf("中序遍历:\n");
    BTreeQrder(root);
    printf("\n");

    //后序遍历
    printf("后序遍历:\n");
    BTreeBack(root);
    printf("\n");

    printf("二叉树层序遍历结果为:");
    LevelOrder(root);
    printf("\n");
     

    printf("二叉树节点个数为:%d\n", BinaryTreeSize(root));
    printf("二叉树叶子节点个数为:%d\n", BinaryTreeLeafSize(root));
    printf("二叉树第3层节点个数为:%d\n", BinaryTreeLevelKSize(root, 3));

    printf("二叉树是否是完全二叉树:%s\n", BinaryTreeComplete(root) ? "是" : "不是");

    BTNode* findNode = BinaryTreeFind(root, 'H');
    if (findNode != NULL)
        printf("查找值为'H'的节点成功。\n");
    else
        printf("未找到该节点的值为'H'。\n");

 
    BinaryTreeDestory(&root);

    return 0;
}

代码前序构建图:
【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言
运行代码图
如果数组的N改为#,或者任意符号,只需将以下代码修改

//通过前序遍历的数组构建二叉树
BTNode* PreBinaryTreeCreateHelper(BTDataType* a, int n, int* pi)
{
	if (*pi >= n || a[*pi] == '#')//这里替换修改即可
	{
		(*pi)++;
		return NULL;
	}
	BTNode* newNode = BuyBTNode(a[(*pi)++]);
	if (newNode != NULL)
	{
		newNode->_left = PreBinaryTreeCreateHelper(a, n, pi);
		newNode->_right = PreBinaryTreeCreateHelper(a, n, pi);
	}

	return newNode;
}

【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言


🚩总结

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘,可以点点关注和赞哦 💓 💗 💕 💞
【算法与数据结构】深入二叉树实现超详解(全源码优化),数据结构;从练气期到筑基期,数据结构,算法,时间复杂度,二叉树,c语言文章来源地址https://www.toymoban.com/news/detail-847195.html

到了这里,关于【算法与数据结构】深入二叉树实现超详解(全源码优化)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】哈夫曼编码(最优二叉树)实现

    哈夫曼编码 等长编码:占的位置一样 变长编码(不等长编码):经常使用的编码比较短,不常用的比较短 最优:总长度最短 最优的要求:占用空间尽可能短,不占用多余空间,且不能有二义性 这里给出哈夫曼二叉树的实现: HuffmanTree.h: 测试数据(主函数): 运行结果截图

    2024年02月16日
    浏览(47)
  • 【数据结构与算法】哈夫曼编码(最优二叉树实现

    哈夫曼编码 等长编码:占的位置一样 变长编码(不等长编码):经常使用的编码比较短,不常用的比较短 最优:总长度最短 最优的要求:占用空间尽可能短,不占用多余空间,且不能有二义性 这里给出哈夫曼二叉树的实现: HuffmanTree.h: 测试数据(主函数): 运行结果截图

    2024年02月16日
    浏览(45)
  • 深入理解数据结构第三弹——二叉树(3)——二叉树的基本结构与操作

    二叉树(1): 深入理解数据结构第一弹——二叉树(1)——堆-CSDN博客 二叉树(2): 深入理解数据结构第二弹——二叉树(2)——堆排序及其时间复杂度-CSDN博客 前言: 在前面我们讲了堆及其应用,帮助我们初步了解了二叉树的一些原理,但那与真正的二叉树仍有不同,

    2024年04月09日
    浏览(49)
  • 数据结构与算法——二叉树+带你实现表达式树(附源码)

    📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段, 因为最近参加新星计划算法赛道(白佬),所以加快了脚步,果然急迫感会增加动力 ——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程,会持续分享学习成果和小项目的 📖作者主页:king南星 📖

    2024年01月25日
    浏览(50)
  • 数据结构与算法—二叉树数组表示(ArrayBinTree)、二叉树的链式表示(LinkedBinTree)的基于C++模板代码实现

    1、二叉树的顺序表示:ArrayBinTree.h 二叉树的顺序表示法操作方便,但缺点是容易造成存储空间浪费。 这是一个用数组实现的二叉树类模板。它可以创建一个空树,也可以在指定的位置插入结点并设置结点的值,可以删除子树,并支持逐层遍历。使用该类时,需要提供一个元

    2024年02月06日
    浏览(44)
  • C++数据结构与算法详解:链表、栈、队列、树、二叉树和图结构的实现与应用

    链表是一种常见的数据结构由一系列节点按顺序连接而成,一般每个节点包含一个数据元素和一个指向下一个节点的引用。 链表有多种类型: 单向链表:每个节点只有一个指向下一个节点的引用 双向链表:每个节点有一个指向前一个节点和一个指向后一个节点的引用 循环链

    2024年02月04日
    浏览(52)
  • 【算法与数据结构】二叉树的三种遍历代码实现(下)—— 非递归方式实现(大量图解)

     上篇: 【算法与数据结构】二叉树的三种遍历代码实现(上)—— 用递归序知识点讲解_Hacynn的博客-CSDN博客 https://blog.csdn.net/zzzzzhxxx/article/details/133609612?spm=1001.2014.3001.5502 目录 前言 1、先序遍历 1.1、详细图解描述 1.2、先序遍历非递归代码实现  2、中序遍历 2.1、详细图解描

    2024年02月08日
    浏览(38)
  • 深入理解数据结构第一弹——二叉树(1)——堆

    前言: 在前面我们已经学习了数据结构的基础操作:顺序表和链表及其相关内容,今天我们来学一点有些难度的知识—— 数据结构中的二叉树 ,今天我们先来学习 二叉树中堆 的知识,这部分内容还是非常有意思的,下面我们就开始慢慢学习 准备工作:本人习惯将文件放在

    2024年04月17日
    浏览(42)
  • 深入浅出二叉树— C语言版【数据结构】

    目录 ​编辑 1.树概念及结构 1.1树的概念 1.2 树的相关概念 ​1.3 树的表示 2.二叉树概念及结构   2.1概念 2.2 特殊的二叉树 2.3 二叉树的性质  2.4 简单二叉树题目练习  2.5 二叉树的存储结构 2.5.1 顺序存储——堆 2.5.2 链式存储 树是一种 非线性的数据结构 ,它是由n(n=0)个有

    2024年02月03日
    浏览(78)
  • 【算法与数据结构】二叉树的三种遍历代码实现(上)—— 用递归序知识点讲解

      本篇博客 (上篇) 先带大家学习 递归方式 进行三种遍历, 而在后续的 (下篇) 中将为大家详细讲解非递归的三种遍历方式。 目录 1、二叉树 2、二叉树的递归遍历 2.1、先序遍历 2.2、中序遍历 2.3、后序遍历  二叉树(Binary tree)是树形结构的一个重要类型。许多实际问

    2024年02月08日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包