数据结构------二叉树简单介绍及实现

news/2024/9/20 14:10:22 标签: 数据结构, 算法

如果不是满二叉树或者完全二叉树,就要用链式存储

//搜索二叉树:左子树的所有值比根小,右子树的所有值比根大

//                      实现查找,最多找高度次(类似二分法)

//二分查找存在的问题:排序;必须对数组操作,插入删除不方便。

//同一个函数不同的值递归占用的空间是一样的,因为销毁了之后再接着用

//不能递归的深度太深,不然会导致栈溢出

//这段二叉树的递归代码编译完了之后是一份指令,这份指令会自己调用自己,不断建立栈帧,然后销毁栈帧

//一些基本知识点

满二叉树每层节点个数构成一个等比数列

增加一个度为1的节点,一定减少一个度为0的节点。增加一个度为2的节点,一定增加一个度为0的,减少一个度为1的。因为0变到1再变到2。

//由性质3得:

//注意:完全二叉树度为1的节点只可能为1或0

//用满二叉树公式来推,计算一个大概的范围

//根据前序.中序序列写出二叉树

//前序确定根,中序分割左右子树

//一定注意7是右子树,因为中序是左根右

//递归的逻辑过程

//递归的物理过程

//通过ebp(高地址,用来保存主调函数的下一行指令)和esp(低地址,不断建立栈帧开辟空间)

//static静态变量只初始化一次,在多次调用的时候会出现问题,可以改为指针或全局变量(最好不要用),全局变量每次使用的时候记得进行初始化

//求节点个数采用分治思想:分而治之,上一层来指挥下一层

//求叶子节点个数:

//这样写就出错了,当该树为空或者没有左/右结点的时候,访问空指针的下一个节点就崩溃了

//所以应该单独讨论节点为空的时候

//求二叉树高度:

//错误做法:效率问题,超出时间限制

计算左/右子树高度值,左边递归到6,6的左右节点都为0,然后6的值为1。

然后回溯到3,3的左子树返回0,右子树返回1,相比较右子树高度大,即计算右子树高度+1,又因为此时并没有保存6的高度为1这个值,还要继续计算3的右子树6的左子树和右子树的高度值再比较然后再回溯到3,把值+1赋值给3这个节点。

然后3向上回溯到2,2的左子树的高度值为2,右子树高度值为0,相比较应该取左子树的高度值。但此时这个值并没有保存下来,应该再计算3的高度值,此时3的高度值未知,要比较3的左右子树的高度值......(计算方法同上一段)

//综述:节点深度越深,该节点计算次数成倍数增加

//正确做法(把值记录下来):

//总的代码:

//实现二叉树
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

BTNode* BuyNode(int x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

BTNode* CreatBinaryTree()
{
	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(6);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node5->right = node7;

	return node1;
}

//遍历只需要注意顺序就好,打印的都是data
//前序遍历
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}
//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

int fun(int n)
{
	if (n == 0)
		return 0;

	return fun(n - 1) + n;
}

// 错误示范
//错因:不能用静态变量,因为如果调用多次这个函数的话,size是一直累加的
//     不能计算出每一次调用时的节点个数
//int TreeSize(BTNode* root)
//{
//	static int size = 0;
//	if (root == NULL)
//		return 0;
//	else
//		++size;
//
//	TreeSize(root->left);
//	TreeSize(root->right);
//
//	return size;
//}
//以下两种解法太复杂
//解法1:把它变成全局变量,并且要注意在每次主函数中调用该函数之前要把size初始化为零
//int size = 0;
//int TreeSize(BTNode* root)
//{
//	if (root == NULL)
//		return 0;
//	else
//		++size;
//
//	TreeSize(root->left);
//	TreeSize(root->right);
//
//	return size;
//}
//解法2:把size的指针传进去,通过指针解引用再++来改变size的值
//void TreeSize(BTNode* root, int* psize)
//{
//	if (root == NULL)
//		return 0;
//	else
//		++(*psize);
//
//	TreeSize(root->left, psize);
//	TreeSize(root->right, psize);
//}

//求节点个数
//采用分治递归的思想
//1.该节点不存在:节点数为零
//2.该节点存在,节点数=左子树节点数+右子树节点数+1
int TreeSize(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left) + TreeSize(root->right) + 1;
}

//求叶子节点个数
int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
		return 1;

	return TreeLeafSize(root->left)
		+ TreeLeafSize(root->right);
}

//求深度(树高)
int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

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

// 有效率问题
int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	return TreeHeight(root->left) > TreeHeight(root->right) ?
		TreeHeight(root->left) + 1 : TreeHeight(root->right) + 1;
}

//int fmax(int x, int y)
//{
//	return x > y ? x : y;
//}

//int TreeHeight(BTNode* root)
//{
//	if (root == NULL)
//		return 0;
//
//	return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
//}

int main()
{
	BTNode* root = CreatBinaryTree();
	PrevOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	//递归太深导致栈溢出
	//printf("%d\n", fun(10000));

	/*int size = 0;
	TreeSize(root, &size);
	printf("TreeSize:%d\n",size);

	size = 0;
	TreeSize(root, &size);
	printf("TreeSize:%d\n", size);*/

	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeLeafSize:%d\n", TreeLeafSize(root));
	printf("TreeHeight:%d\n", TreeHeight(root));

	return 0;
}


http://www.niftyadmin.cn/n/5667212.html

相关文章

分类预测 | Matlab实现SMA-CNN-SVM黏菌算法优化卷积支持向量机分类预测

分类预测 | Matlab实现SMA-CNN-SVM黏菌算法优化卷积支持向量机分类预测 目录 分类预测 | Matlab实现SMA-CNN-SVM黏菌算法优化卷积支持向量机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SMA-CNN-SVM黏菌算法优化卷积支持向量机分类预测&#xff0…

Activiti7《第三式:破刀式》——工作流中的刀锋利刃

冲冲冲!开干 这篇文章将分为九个篇章,带你逐步掌握工作流的核心知识。欢迎来到 “破刀式” 篇章!在工作流的开发过程中,锋利的利器就是 精湛的设计与代码优化。本篇文章将探讨如何像一把利刃一样,用最直接的方式切入复…

JVM 内存模型:堆、栈、方法区讲解

1. 引言 Java 虚拟机(JVM)的内存模型是 Java 程序运行时的基础之一。JVM 内存模型主要包括 堆、栈、和 方法区。它们各自有不同的作用和管理方式,并且影响着程序的性能和稳定性。为了更好地理解 JVM 的内存管理机制,我们将结合电…

MATLAB系列03:分支语句和编程设计

MATLAB系列03:分支语句和编程设计 3. 分支语句和编程设计3.1 自上而下的编程方法简介3.2 伪代码的应用3.3 关系运算符和逻辑运算符3.3.1 关系运算符3.3.2 小心和~运算符3.3.3 逻辑运算符3.3.4 逻辑函数 3.4 选择结构3.4.1 if结构3.4.2 switch结构3.4.3 try/catch结构…

中电金信:构建银行级数智化平台 做好数字金融大文章

近年来,党中央高度重视数字经济发展,2023年召开的中央金融工作会议将数字金融列为金融“五篇大文章”之一,要求着力打造现代金融机构和市场体系,疏通资金进入实体经济的渠道,为数字金融建设指明了目标方向。新形势下&a…

Java 集合(数据结构)面试题总结

一、栈和队列的区别 具体的场景应用 栈(Stack)和队列(Queue)是两种基本的数据结构,它们在数据的存取顺序上有不同的规则。以下是它们的主要区别以及具体的应用场景。 1. 栈(Stack) 特点&…

2024 屡发屡中的论文方向:时空预测!

【时空预测】是一种专门处理具有时间和空间属性的数据的分析技术,随技术发展,用于解决复杂的时空问题的新预测方法和模型不断涌现。仅2024上半年,时空大模型UrbanGPT、通用城市时空预测模型UniST、即插即用的时空提示调整机制FlashST……各大…

我的AI工具箱Tauri版-VideoReapeat视频解说复述克隆

本教程基于自研的AI工具箱Tauri版进行VideoReapeat视频解说复述克隆。 VideoReapeat视频解说复述克隆 是自研的AI工具箱Tauri版中的一款专用模块,旨在通过AI技术对视频解说内容进行复述和克隆。该工具可自动洗稿并重新生成视频解说,通过简单配置即可对大…