详细认识二叉树【图片+代码】

news2024/11/16 13:26:18

目录

一、树的概念及结构

1.1树的概念

1.2树的相关概念

1.3树的表示

1.4树在实际中的应用(目录树) 

二、二叉树概念及结构

2.1概念

2.2特殊的二叉树

2.3二叉树的性质 

2.4二叉树存储结构

三、二叉树的顺序结构及实现

3.1二叉树的顺序结构

3.2堆的结构及概念

四、二叉树的链式结构及实现

五、二叉树的遍历(前序、中序、后序、层序遍历)

5.1前序遍历

5.2中序遍历

5.3后序遍历

5.4层序遍历

六、求二叉树的节点个数、叶子节点个数、高度、查找节点等功能

6.1二叉树节点的个数

6.2二叉树叶子节点的个数

6.3二叉树的高度

6.4二叉树第K层节点的个数

6.5查找节点

6.6二叉树度为1的节点个数

七、创建二叉树、销毁二叉树

7.1创建二叉树

7.2销毁二叉树

八、判断二叉树是否为完全二叉树

九、OJ练习——二叉树部分

9.1检查两颗树是否相同

9.2单值二叉树

9.3翻转二叉树

9.4对称二叉树

9.5另一颗数的子树

9.6判断一颗二叉树是否是平衡二叉树


一、树的概念及结构

1.1树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的
  • 有一个特殊的结点,称为根结点,根节点没有前驱结点。
  • 除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。
  • 因此,树是递归定义的。

1.2树的相关概念

  1. 节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
  2. 叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I...等节点为叶节点
  3. 非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G...等节点为分支节点
  4. 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
  5. 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
  6.  兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点 
  7. 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
  8. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  9. 树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
  10. 堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点
  11. 节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
  12. 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
  13. 森林:由m(m>0)棵互不相交的树的集合称为森林;

1.3树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之间 的关系,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。
typedef int DataType;
struct Node
{
struct Node* _firstChild1;                 // 第一个孩子结点
struct Node* _pNextBrother;            // 指向其下一个兄弟结点
DataType _data;                               // 结点中的数据域
};

1.4树在实际中的应用(目录树) 

二、二叉树概念及结构

2.1概念

一棵二叉树是结点的一个有限集合,该集合:
  1. 或者为空
  2. 由一个根节点加上两颗别称为左子树和右子树的二叉树组成

注意: 

  1. 二叉树不存在度大于2的节点
  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
  3. 对于任意的二叉树都是由以下几种情况复合而成的:

2.2特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k - 1,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

2.3二叉树的性质 

2.4二叉树存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
        1.顺序存储:顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为
                             不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储关
                             于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑
                              上是一颗二叉树。

         2.链式存储:二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的                               逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域                               左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

                             链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链。

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
        struct BinTreeNode* _pLeft;         // 指向当前节点左孩子
        struct BinTreeNode* _pRight;       // 指向当前节点右孩子
        BTDataType _data;                       // 当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
        struct BinTreeNode* _pParent;         // 指向当前节点的双亲
        struct BinTreeNode* _pLeft;              // 指向当前节点左孩子
        struct BinTreeNode* _pRight;            // 指向当前节点右孩子
        BTDataType _data;                            // 当前节点值域
};

三、二叉树的顺序结构及实现

3.1二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

3.2堆的结构及概念

有关堆的结构及概念,堆的实现和应用,请看上一篇:http://t.csdn.cn/Dbj44

四、二叉树的链式结构及实现

树的简单的创建:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
        BTDataType _data;
        struct BinaryTreeNode* _left;
        struct BinaryTreeNode* _right;
}BTNode;
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);
        node1->_left = node2;
        node1->_right = node4;
        node2->_left = node3;
        node4->_left = node5;
        node4->_right = node6;
        return node1;
}

五、二叉树的遍历(前序、中序、后序、层序遍历)

5.1前序遍历

前序遍历——访问根结点的操作发生在遍历其左右子树之前。

代码:

void PrevOrder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->lchild);
	PrevOrder(root->rchild);
}

递归展开图:

5.2中序遍历

中序遍历——访问根结点的操作发生在遍历其左右子树之中(间)。

代码:

void InOrder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->lchild);
	printf("%d ", root->data);
	InOrder(root->rchild);
}

递归展开图:

5.3后序遍历

后序遍历——访问根结点的操作发生在遍历其左右子树之后。

代码:

void PostOrder(TNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->lchild);
	PostOrder(root->rchild);
	printf("%d ", root->data);
}

递归展开图:

5.4层序遍历

层序遍历——设二叉树的根节点所在 层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层 上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

 层序遍历思想:要借助队列来完成,将根节点入队列,然后开始循环,若队列为空就循环结束,每次出来队头的数据,在队头的数据出去之后要立即将出去的那个节点的孩子入队列(孩子为空就不入队列)

代码:

void LevelOrder(TNode* root)
{
	if (root == NULL)
	{
		return;
	}
	Queue q;
	QueueInit(&q);
	//进队列
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		TNode* node = QueueFront(&q);
		QueuePop(&q);
		printf("%d ", node->data);
		if (node->lchild)
			QueuePush(&q, node->lchild);
		if (node->rchild)
			QueuePush(&q, node->rchild);

	}
	printf("\n");
}

六、求二叉树的节点个数、叶子节点个数、高度、查找节点等功能

6.1二叉树节点的个数

int BTreeSize(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	return BTreeSize(root->lchild) + BTreeSize(root->rchild) + 1;
}

6.2二叉树叶子节点的个数

int BTreeLeafSize(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->lchild == NULL && root->rchild == NULL)
	{
		return 1;
	}
	return BTreeLeafSize(root->lchild) + BTreeLeafSize(root->rchild);
}

6.3二叉树的高度

int BTreeHeight(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	int leftHeight = BTreeHeight(root->lchild);
	int rightHeight = BTreeHeight(root->rchild);

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

6.4二叉树第K层节点的个数

int BTreeLevelKSize(TNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BTreeLevelKSize(root->lchild, k - 1) + BTreeLevelKSize(root->rchild, k - 1);
	
}

6.5查找节点

TNode* BTreeFind(TNode* root, BTDataType x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	TNode* left = BTreeFind(root->lchild, x);
	if (left)
		return left;
	TNode* right = BTreeFind(root->rchild, x);
	if (right)
		return right;

	return NULL;
}

6.6二叉树度为1的节点个数

int BTreeDeg1Size(TNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int left = BTreeDeg1Size(root->lchild);
	int right = BTreeDeg1Size(root->rchild);
	if ((root->lchild == NULL && root->rchild != NULL)
		|| (root->lchild != NULL && root->rchild == NULL))
	{
		return left + right + 1;
	}
	else
	{
		return left + right;
	}
}

七、创建二叉树、销毁二叉树

7.1创建二叉树

 

TNode* TCreateTree(char* tree, int* pi)
{
	if (tree[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

	TNode* root = (TNode*)malloc(sizeof(TNode));
	root->data = tree[*pi];
	(*pi)++;

	root->lchild = TCreateTree(tree, pi);
	root->rchild = TCreateTree(tree, pi);
	return root;
}

7.2销毁二叉树

void DestoryTree(TNode* root)
{
	if (root == NULL)
	{
		return;
	}

	DestoryTree(root->lchild);
	DestoryTree(root->rchild);
	free(root);
}

八、判断二叉树是否为完全二叉树

借助队列,将一个二叉树层序遍历进入队列,遇到NULL也进入队列,直到出队列时遇到NULL停止,判断此时队列中是否存在非NULL元素,如果存在,不是完全二叉树,如果全为NULL,是完全二叉树。

bool BTreeComplete(TNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		TNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueuePush(&q, front->lchild);
			QueuePush(&q, front->rchild);
		}

		else
		{
			break;
		}
	}
	while (!QueueEmpty(&q))
	{
		TNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front != NULL)
		{
			QueueDestory(&q);
			return false;
		}
	}
	QueueDestory(&q);
	return true;
}

九、OJ练习——二叉树部分

9.1检查两颗树是否相同

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p == NULL && q == NULL)
    {
        return true;
    }
    if(p == NULL || q == NULL)
    {
        return false;
    }
    if(p->val != q->val)
    {
        return false;
    }
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}

 

9.2单值二叉树

bool isUnivalTree(struct TreeNode* root){
    if(root == NULL)
    {
        return true;
    }
    if(root->left!= NULL && root->val != root->left->val)
    {
        return false;
    }
    if(root->right!= NULL && root->val != root->right->val)
    {
        return false;
    }
    return isUnivalTree(root->left) && isUnivalTree(root->right);
}

9.3翻转二叉树

struct TreeNode* invertTree(struct TreeNode* root){
    if(root == NULL)
    {
        return NULL;
    }

    //保存一下原来的左孩子
    struct TreeNode* left = root->left;
    root->left = invertTree(root->right);
    root->right = invertTree(left);
    return root;
}

9.4对称二叉树

bool _isSymmetric(struct TreeNode* r1, struct TreeNode* r2)
{
    if(r1 == NULL && r2 == NULL)
    {
        return true;
    }
    if(r1 == NULL || r2 == NULL)
    {
        return false;
    }
    if(r1->val != r2->val)
    {
        return false;
    }
    return _isSymmetric(r1->left, r2->right) && _isSymmetric(r1->right, r2->left);
}

bool isSymmetric(struct TreeNode* root){

    return _isSymmetric(root->left, root->right);
}

9.5另一颗数的子树

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
    if(p == NULL && q == NULL)
    {
        return true;
    }
    if(p == NULL || q == NULL)
    {
        return false;
    }
    if(p->val != q->val)
    {
        return false;
    }
    return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root == NULL && subRoot == NULL)
    {
        return true;
    }
    if(root == NULL || subRoot == NULL)
    {
        return false;
    }
    if(isSameTree(root, subRoot))
    {
        return true;
    }
    return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}

9.6判断一颗二叉树是否是平衡二叉树

 

int BTreeHeight(struct TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

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

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

bool isBalanced(struct TreeNode* root){
    if(root == NULL)
    {
        return true;
    }
    int left = BTreeHeight(root->left);
    int right = BTreeHeight(root->right);
    int sub = abs(left - right);
    if(sub > 1)
    {
        return false;
    }
    return isBalanced(root->left) && isBalanced(root->right);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/700768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis6之集群

集群&#xff0c;就是通过增加服务器的数量&#xff0c;提供相同的服务&#xff0c;从而让服务器达到一个稳定、高效的状态 必要性 单个redis存在不稳定性。当redis服务宕机了&#xff0c;就没有可用的服务了。而且单个redis的读写能力是有限的。使用redis集群可以强化redis的…

PIL.Image 调色板模式处理标签数据

文章目录 1 使用PIL.Image库进行调色板模式2 转回原来的色彩3 效果参考 1 使用PIL.Image库进行调色板模式 基本步骤&#xff1a; 自定义调色板&#xff0c;数据格式是一个Nx3的二维数组&#xff0c;一维数组的位置为分类的下标数据类型为np.uint8转化为调色板模式后img.conve…

想知道音频怎么转文字吗?

随着数字化技术的不断发展&#xff0c;我们生活中产生的各种音频越来越多&#xff0c;例如会议录音、采访录音等等。虽然音频记录信息方便&#xff0c;但它们在信息处理、存储和分享方面也存在问题。比如当我们需要对音频中的内容进行编辑或整理时&#xff0c;手动打字出现漏字…

Eclipse中项目的配置

1、修改本地运行时Tomcat对应的JRE版本 老项目升级JDK&#xff0c;在eclipse修改了项目的jdk、编译等级&#xff0c;但还是启动失败&#xff0c;报“java.lang.UnsupportedClassVersionError”。 观察发现&#xff0c;启动日志&#xff0c;tomcat还是使用的jdk1.5&#xff0c;…

编程题分享:有⼀堆糖果,其数量为n,现将糖果分成不同数量的堆数

背景 近期面试遇到一家公司的编程题&#xff0c;觉得挺有参考价值 此处使用 PHP语言&#xff0c;进行编码测试&#xff0c; 编码之前要进行思路分析&#xff0c;避免无头苍蝇&#xff0c;走一步看一步 最后&#xff0c;希望后期面试顺利&#xff01;欢迎指摘 . 题目&#xff1…

形态学操作之膨胀

note // 膨胀原理&#xff1a;操作过程中&#xff0c;若膨胀因子某点是1&#xff0c;且原图该点为1&#xff0c;则锚点位置为1 code // 膨胀 // 膨胀原理&#xff1a;操作过程中&#xff0c;若膨胀因子某点是1&#xff0c;且原图该点为1&#xff0c;则锚点位置为1 typedef e…

gma 2 教程(一)概述:1.GMA 简介

地理与气象分析库&#xff08;Geographic and Meteorological Analysis. gma&#xff09;&#xff0c;是一个基于 Python 的地理、气象数据快速处理分析和地理制图函数包。构建过程参考了ArcGIS和QGIS的操作逻辑和特点&#xff0c;并添加诸多独创性、独有的功能&#xff0c;具有…

QT Creator上位机学习(三)QString及其相关控件介绍

系列文章目录 文章目录 系列文章目录字符串QStringQLableQLineEditQString的常用功能 字符串QString QSting类&#xff0c;用于处理字符串&#xff0c;进行字符串和数字之间的转化 转换函数&#xff1a; //字符串转数字 QString str......; int numstr.toInt(); float num2s…

微信项目IO优化:Jetpack DataStore 与MMKV实现高效持久化

咱们一起来看看mmkv 腾讯微信团队于2018年9月底宣布开源 MMKV &#xff0c;原理上基于 mmap的一个高性能key-value 组件&#xff0c;数据的序列化/反序列化使用 protobuf 实现&#xff0c;主打高性能和稳定性&#xff0c;也支持了Android 平台。 **MMKV最新源码托管地址&#…

Mybatis【核心配置文件说明】

配置解析 1、核心配置文件 mybatis-config.xml 注意&#xff1a; 在配置 mybatis-config.xml 时&#xff0c;要注意标签的顺序&#xff1a; 2、环境变量&#xff08;environments&#xff09; MyBatis 可以配置成适应多种环境 不过要记住&#xff1a;尽管可以配置多个环境…

现代操作系统(下)

第六章 死锁 在计算机系统中有很多独占性的资源&#xff0c;在任一时刻它们都只能被一个进程使用。两个进程同时使用同一文件系统表中的表项会引起文件系统的瘫痪。正因为如此&#xff0c;操作系统都具有授权一个进程&#xff08;临时&#xff09;排他地访问某一种资源的能力。…

【AcWing】夏季每日一题2023 -- 4382. 快速打字 -- Java Version

题目链接&#xff1a;https://www.acwing.com/problem/content/4385/ 1. 题解&#xff08;4382. 快速打字&#xff09; y总视频讲解&#xff1a;https://www.acwing.com/video/4743/ 类似题目&#xff1a;AcWing 2816. 判断子序列 1.1 双指针&#xff1a;判断子序列 ⭐ 时间复…

一文吃透MAUI、WinUI3和WPF的优势及劣势

引言&#xff1a; 在应用程序开发领域&#xff0c;选择合适的框架对于开发人员和业务来说至关重要。本文将比较并对比三个流行的用户界面框架&#xff1a;MAUI&#xff08;Multi-platform App UI&#xff09;、WinUI 3和WPF&#xff08;Windows Presentation Foundation&#x…

element ui table表格(表头、表体)样式

效果图如下 1.表头样式&#xff1a;header-cell-stayle 2.表体样式 在methods中写 在style中

使用Qt/C++实现WGS84、高德GCJ-02、百度BD-09坐标系间相互转化

在做地图相关开发时候&#xff0c;绕不开不同坐标系间的转化&#xff0c;因此我根据查阅相关资料后将不同坐标系间的转换封装到一个GeoTranslate类中&#xff0c;该类转换函数不仅支持Qt/C调用&#xff0c;同时可在QML中直接调用&#xff0c;配合上QML/Map很方便&#xff0c;我…

大咖培训讲座|高性能计算专场 认识真正的高性能计算,探究如何成为高性能计算工程师 ...

猿代码科技高性能计算讲座原文——OpenBLAS创始人无保留分享 我们如何成为一个高性能计算的工程师&#xff0c;或者这种性能优化的这一部分的软件工程师。 我其实想了一下&#xff0c;这一部分的入门的基础其实没那么难。 就是更主要的话&#xff0c;我还是在于兴趣和自驱力…

idea自定义类注释以及方法注释,无警告

背景 idea&#xff1a;IntelliJ IDEA 2023.1.3 (Ultimate Edition) 效果 类 方法 正式&#xff1a;类 设置 代码 /** * author: 你的名字* date: ${DATE} on ${TIME}* desc: $NAME*/注意&#xff0c;请全部复制&#xff0c;空行也要&#xff0c;看设置截图选中部分 …

Jenkins定时执行JMeter脚本并查看测试报告实现

【整体思路】 通过Tomcat启动Jenkins服务&#xff0c;在Jenkins中创建自动化测试项目&#xff0c;项目配置中配置构建时执行的动作&#xff0c;配置构建后动作展示报告 【涉及软件及版本信息】 Tomcat版本&#xff1a;9.0.76 Jenkins版本&#xff1a;2.332.3&#xff08;注…

ES批量上传数据 - Python操作ES

代码 # -*- coding: utf-8 -*- # Author : markadcimport randomfrom elasticsearch import Elasticsearch, helpersurl http://localhost:9200/ client Elasticsearch(url) index testsome [] for i in range(1, 10001):docu {uid: i, age: random.randint(1, 100)} …

ChatGPT中的Token:了解聊天模型的基本单位

✍创作者&#xff1a;全栈弄潮儿 &#x1f3e1; 个人主页&#xff1a; 全栈弄潮儿的个人主页 &#x1f3d9;️ 个人社区&#xff0c;欢迎你的加入&#xff1a;全栈弄潮儿的个人社区 &#x1f4d9; 专栏地址&#xff1a;AI大模型 ChatGPT中的Token是什么&#xff1f; 在ChatGPT…