二叉树的初步学习

前言

对于二叉树的学习不想其他数据结构一样,直接学习他的结构的构建。单纯的一个二叉树在实际中没什么作用,除非是加了限制条件的,比如大名鼎鼎的红黑树。但是对于初学者而言,刚开始就学习红黑树,会让你刚接触就想放弃。难度太大了。所以,对于新手学习二叉树,学习的是他的遍历和搜索功能。

正文

对于二叉树的学习,一种是堆结构,它适用于完全二叉树,底层一般是数组,但是局限性就是适用于堆结构;还有一种就是链式结构,左孩子右父亲,他几乎适用于所有的二叉树,但是要记住单纯的二叉树没有什么实际作用。我们这里介绍的是第二种

简单构建与销毁

虽然我们这里不学习他的构建,但是我们还是要简单搭建一下,帮助我们学习他的遍历与搜索。最后也要把他销毁,有始有终,不能只管生不管养。

构建

cpp 复制代码
//构建
typedef int BTDataType;
typedef struct TreeNode
{
    BTDataType data;
    struct TreeNode* left;
    struct TreeNode* right;
}BTNode;
BTNode* CreateNode(BTDataType data)
{
    BTNode* node = (BTNode*)malloc(sizeof(BTNode));
    node->data = data;
    node->left = NULL;
    node->right = NULL;
    return node;

}
//手搓二叉树
BTNode* CreateTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);
	BTNode* node8 = BuyNode(8);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	//node3->left = node7;
	//node7->left = node8;
	return node1;
}

销毁

cpp 复制代码
//一级指针 使用这自己置空
void BTNDestroy(BTNode* root)
{
    if(root == NULL)
    {
        return;
    }
    BTNDestroy(root->left);
    BTNDestroy(root->right);
    free(root);
}

这里销毁二叉树也可以用二级指针,用二级指针就可以直接置空。

四大遍历

这里的前序 中序 后序 采用的都是递归思路,层序采用的是队列结构

前序遍历(根 左子树 右子树

这里的递归展开图,建议自己去画画,可以加深你的理解。 中序和后序和这里是一样的。我就不画图啦

cpp 复制代码
//前序遍历
void BTNPrevOrder(BTNode* root)
{
    if(root==NULL)
    {
        printf("NULL ");
        return;
    }
    printf("%d ",root->data);
    BTNPrevOrder(root->left);
    BTNPrevOrder(root->right);
}

中序遍历(左子树 根 右子树

cpp 复制代码
//中序遍历
void BTNInOrder(BTNode* root)
{
    if(root==NULL)
    {
        printf("NULL ");
        return;
    }
    BTNInOrder(root->left);
    printf("%d ",root->data);
    BTNInOrder(root->right);
}

后序遍历(左子树 右子树

cpp 复制代码
//后序遍历
void BTNPostOrder(BTNode* root)
{
    if(root==NULL)
    {
        printf("NULL ");
        return;
    }
    BTNPostOrder(root->left);
    BTNPostOrder(root->right);
    printf("%d ",root->data);
}

层序遍历

这里的层序遍历需要使用一个队列,先进先出的特点非常适合这里的层序遍历,这里我是用的是c语言,直接手搓了一个队列。如果是c++的话,就可以直接调用库函数的。

cpp 复制代码
//层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!(QueueEmpty(&q)))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", front->data);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}

	QueueDestroy(&q);
}

搜索与查询

这里大部分的查询都是递归,如果和层序有关的一般就会适用于队列

求第k层的节点数

cpp 复制代码
//求第k层的节点数
//树的第k层节点个数 = 左子树的k-1层个数 + 右子树的k-1层个数
int TreeLevel(BTNode* root, int k)
{
	assert(k > 0);
	if (root == nullptr)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return TreeLevel(root->left, k - 1) + TreeLevel(root->right, k - 1);
}

查询值为x的节点

这里思路还是递归,采用分支思想,从上开始往下比。

cpp 复制代码
//查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == nullptr)
	{
		return nullptr;
	}
	if (root->data == x)
	{
		return root;
	}
	BTNode* lret = BinaryTreeFind(root->left, x);
	if (lret)
	{
		return lret;
	}
	BTNode* rret = BinaryTreeFind(root->right, x);
	if (rret)
	{
		return rret;
	}
	return nullptr;
}

查询二叉树的高度/深度

这里递归的思路,不要过于追求简介,要保存上次递归得到的高度,否则你每次查看你的高度都要重新递归一遍,这是很恐怖的,爆炸式增长。

cpp 复制代码
//天坑写法
// 这里要执行的次数可不是简单的几次,每次返回值获取的时候都要重新递归一遍
// 最底层的员工要执行2^n次,这是个非常恐怖的时间复杂度 爆炸式增长
// 所以,写代码的时候不要过度追求三目操作符 外行人看来很酷 内行人看你就像看杀马特一样
//int TreeHeight(BTNode* root)
//{
//	if (root == nullptr)
//	{
//		return 0;
//	}
//	return TreeHeight(root->left) > TreeHeight(root->right) ? TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
//}
int TreeHeight(BTNode* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

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

查询二叉树的节点数

这里的递归思路是,让最底层的节点干活,就像一个学校汇报人数一样,校长让教导主任干,教导主任让班主任干,层层递进。

cpp 复制代码
//这里求size比较挫的法子是多弄一个参数 或者全局变量
//int size = 0;
//void TreeSize(BTNode* root)
//{
//	if (root == nullptr)
//	{
//		return;
//	}
//	size++;
//	TreeSize(root->left);
//	TreeSize(root->right);
//}
//优秀的解放 分治
int TreeSize(BTNode* root)
{
	return root == nullptr ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

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

这里的思路不是递归,是采用队列层序遍历节点,如果遇到空节点,就退出循环,开始判断剩余的队列是否还有非空节点,如果有那就不是完全二叉树,反之,就是完全二叉树。这里的队列完全适合了完全二叉树的特性

cpp 复制代码
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}

	//判断是否是
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return 0;
		}

	}
	return 1;
	QueueDestroy(&q);
}

总结

二叉树的学习会用到大量的递归,这里也是练习递归的一个很好的章节。也许你可能会感到使用递归有点困难。这是因为你还没有理会到递归的精髓。这里实际上使用递归解决这些遍历是很简单的,如果使用非递归解决二叉树的问题,那才是真正的上难度了。

相关推荐
Angindem4 分钟前
websoket 学习笔记
笔记·学习
Nigori7_4 分钟前
day32-动态规划__509. 斐波那契数__70. 爬楼梯__746. 使用最小花费爬楼梯
算法·动态规划
x_feng_x7 分钟前
数据结构与算法 - 数据结构与算法进阶
数据结构·python·算法
梭七y13 分钟前
【力扣hot100题】(097)颜色分类
算法·leetcode·职场和发展
LVerrrr25 分钟前
Missashe考研日记-day18
学习·考研
月亮被咬碎成星星34 分钟前
LeetCode[541]反转字符串Ⅱ
算法·leetcode
1024熙38 分钟前
【C++】——lambda表达式
开发语言·数据结构·c++·算法·lambda表达式
AI绘画咪酱40 分钟前
【CSDN首发】Stable Diffusion从零到精通学习路线分享
人工智能·学习·macos·ai作画·stable diffusion·aigc
uhakadotcom1 小时前
拟牛顿算法入门:用简单方法快速找到函数最优解
算法·面试·github
老马啸西风1 小时前
Neo4j GDS-09-neo4j GDS 库中路径搜索算法实现
网络·数据库·算法·云原生·中间件·neo4j·图数据库