• 【数据结构】二叉树 二叉树的深度优先遍历和广度优先遍历 完全二叉树和满二叉树的性质 二叉树的节点个数以及叶子节点个数


    1. 树的概念及结构(了解)

    1.1 树的概念

    树是一种非线性的数据结构,它是由 n(n >= 0)个有限个节点组成的一个具有层次关系的集合。把它叫做树是因为他看起来像一颗倒挂的树,也就是说,它是根朝上,而叶朝下的。
    树的定义有两点: 1)有且仅有一个特定的称为根(root)的结点; 2)当结点数量>1时,其余结点可分为 若干个互不相交的有限集 ,其中每个集合也可以看作一颗树,称之为根的子树。

    在这里插入图片描述

    • 有一个特殊的节点,称为根节点(root),根节点没有前驱节点。
    • 除根节点外,其余节点别分为M(M > 0)个互不相交的集合T1、T2……其中每一个集合又是一棵树结构与树类似的子树。每颗子树的根节点有且只有一个前驱,可以有0个或多个后继。
    • 树是递归定义的。

    在这里插入图片描述
    判断是否是一个树

    • 子树是不相交的
    • 出了根节点外,每个节点有且只有一个父节点
    • 一颗N个节点的树有N-1条边

    几个重要的知识点:
    在这里插入图片描述

    • 节点的:一个节点含有的子树的个数称为该节点的度;如上图:A的度为6,B的度为0
    • 叶节点或终端节点:度为0的节点称为叶节点;如上图:B、C、H、I、P……等都是叶节点
    • 非终端节点或分支节点:度不为0的节点;如上图:D、E、F……等节点为分支节点
    • 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;如上图:A是B的父节点
    • 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;如上图:B是A的子节点
    • 兄弟节点:具有相同父节点的节点称为兄弟节点;如上图:B、C是兄弟节点
    • 树的度:一棵树中,最大节点的度称为树的度;如上图:树的度为6
    • 节点的层次:从根开始定义起,根为第一层,根的子节点为第二层,以此类推;
    • 树的高度深度:树中节点的最大层次;如上图:树的高度为4
    • 节点的祖先:从根到该节点所经的分支上所有的节点;如上图:A是所有节点的祖先
    • 子孙:以某点为根的子树中任一节点都称为该节点的子孙;如上图:所有节点都是A的子孙
    • 森林:有m(m>0)棵互不相交的多棵树的集合称为树;(数据结构中的学习并查集本质就是一个森林)

    1.2 树的表示方法

    树的结构相对线性表就相对复杂很多,要存储起来更加复杂。实际中树有很多种表示方法,如:双亲表示法、孩子表示法、孩子兄弟表示法等等

    1. 左孩子右兄弟表示法
    typedef int DataType;
    struct Node
    {
    	struct Node* firstChild1;//第一个孩子节点
    	struct Node* pNextBrother;//指向下一个兄弟节点
    	DataType data;//数据域
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    1. 双亲表示法

    在这里插入图片描述

    1.3 树在实际中的应用

    • 表示文件系统的目录

    在这里插入图片描述

    2. 二叉树的概念及节点(重点)

    2.1 二叉树的概念

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

    在这里插入图片描述

    2.2 二叉树的特点

    1. 每个节点最多有两棵子树,即二叉树不存在度大于2的节点
    2. 二叉树的子树有左右之分,其子树的次序不能颠倒

    在这里插入图片描述

    2.3 特殊的二叉树

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

    在这里插入图片描述
    在这里插入图片描述

    2.5 二叉树的性质

    1. 若规定根节点层数为1,则一颗非空二叉树的第i层上最多有2^(n-1)个节点(满二叉树)
    2. 若规定根节点层数为1,则深度为h的二叉树最大节点数为2^h - 1(满二叉树)
    3. 对任何一颗二叉树,如果度数为0的节点个数为n0,度为2的节点个数为n2,则有n0=n2+1
    4. 若规定根节点层数为1,具有n个节点的满二叉树的深度,h = log2(N+1) (以2为底,(N+1)为对数)
      在这里插入图片描述

    相关习题:
    在这里插入图片描述
    解析2:
    在这里插入图片描述
    解析3:
    在这里插入图片描述

    2.6 二叉树的遍历

    所谓遍历就是指沿着某条搜索路线,依次对树中的每个节点均做一次且仅做一次的访问。遍历二叉树是很重要的运算之一,是二叉树上进行其他运算的基础。

    2.6.1 深度优先遍历

    二叉树的前序、中序、后序遍历也称为深度优先遍历,因为他们都是先往下遍历的,而不管左右。

    遍历方式:

    • 前序遍历(NLR):访问根节点的操作发生在遍历其左右子树之前(根左右)
    • 中序遍历(LNR):访问根节点的操作发生在遍历其左右子树之种(左根右)
    • 后序遍历(LRN):访问根节点的操作发生在遍历其左右子树之后(左右根)

    在这里插入图片描述

    代码实现:

    #include 
    #include 
    
    typedef char BTDataType;
    
    typedef struct BinaryTreeNode
    {
    	struct BinaryTreeNode* left;//左孩子
    	struct BinaryTreeNode* right;//右孩子
    	BTDataType data;//数据域
    }BTNode;
    
    void PrevOrder(BTNode* root)//前序
    {
    	if (root == NULL)
    	{
    		printf("NULL ");
    		return;
    	}
    	printf("%c ", root->data);
    	PrevOrder(root->left);
    	PrevOrder(root->right);
    }
    
    void InOrder(BTNode* root)//中序
    {
    	if (root == NULL)
    	{
    		printf("NULL ");
    		return;
    	}
    	InOrder(root->left);
    	printf("%c ", root->data);
    	InOrder(root->right);
    }
    
    void PostOrder(BTNode* root)//后序
    {
    	if (root == NULL)
    	{
    		printf("NULL ");
    		return;
    	}
    	PostOrder(root->left);
    	PostOrder(root->right);
    	printf("%c ", root->data);
    }
    
    
    int main()
    {
    	BTNode* A = (BTNode*)malloc(sizeof(BTNode));
    	A->data = 'A';
    	A->left = NULL;
    	A->right = NULL;
    
    	BTNode* B = (BTNode*)malloc(sizeof(BTNode));
    	B->data = 'B';
    	B->left = NULL;
    	B->right = NULL;
    
    	BTNode* C = (BTNode*)malloc(sizeof(BTNode));
    	C->data = 'C';
    	C->left = NULL;
    	C->right = NULL;
    
    	BTNode* D = (BTNode*)malloc(sizeof(BTNode));
    	D->data = 'D';
    	D->left = NULL;
    	D->right = NULL;
    
    	BTNode* E = (BTNode*)malloc(sizeof(BTNode));
    	E->data = 'E';
    	E->left = NULL;
    	E->right = NULL;
    
    	A->left = B;
    	A->right = C;
    	B->left = D;
    	B->right = E;
    
    	PrevOrder(A);
    	printf("\n");
    
    	InOrder(A);
    	printf("\n");
    
    	PostOrder(A);
    	printf("\n");
    
    	system("pause");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    在这里插入图片描述

    我们主要要了解具体递归的顺序,我们可以自己在纸上画一画

    2.6.2 广度优先遍历

    层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在的层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的根节点,然后从左到右访问第二层上的节点,以此类推,自上而下,自左至右逐层访问树的节点的过程就是层序遍历,也称为广度优先遍历。

    在这里插入图片描述

    二叉树的广度优先遍历思想:我们利用队列(先进先出),上一层出的时候带着下一层进

    在这里插入图片描述

    //层序遍历	广度优先遍历
    void LevelOrder(BTNode* root)
    {
    	//核心思想——上一层出的时候带下一层节点进
    	queue<BTNode*> q;//创建队列容器
    	if (root)
    	{
    		q.push(root);//入队
    	}
    	while (!q.empty())
    	{
    		BTNode* front = q.front();
    		q.pop();
    		printf("%c ", front->data);
    		if (front->left)
    		{
    			q.push(front->left);
    		}
    		if (front->right)
    		{
    			q.push(front->right);
    		}
    	}
    	printf("\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    二叉树遍历的例题:
    在这里插入图片描述
    答案为:A A D

    2.7 计算二叉树的节点个数

    很多人看到计算二叉树的节点个数,心想那么简单,直接遍历一下二叉树不就好了吗。

    //节点个数
    int size = 0;
    void TreeSize(BTNode* root)
    {
    	if (root == NULL)
    	{
    		return;
    	}
    	else
    	{
    		size++;
    	}
    	TreeSize(root->left);
    	TreeSize(root->right);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    其实,vs2022已经报错了,为什么呢?因为我们定义了一个全局的size,在我们计算了节点为A的节点个数时,得到的大小是5,但是在计算节点为B的节点个数为8,。就是因为我们这个变量A是个全局变量,会累加的。

    void TreeSize(BTNode* root, int* psize)
    {
    	if (root == NULL)
    	{
    		return;
    	}
    	else
    	{
    		++(*psize);
    	}
    	TreeSize(root->left, psize);
    	TreeSize(root->right, psize);
    }
    
    
    	int Asize = 0;
    	TreeSize(A, &Asize);
    	printf("TreeSize(A): %d", Asize);
    
    	int Bsize = 0;
    	TreeSize(B, &Bsize);
    	printf("TreeSize(A): %d", Bsize);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    我们要记得把地址传过去。
    但是,如果我们知道多线程的时候,程序在运行时,可能有多个进程在同时进行,我们就无法保证在求一个节点的数时的节点前,先把size赋值为0了。怎么办呢?看下面的代码就很好的解决了这个问题。

    //返回节点个数
    int TreeSize(BTNode* root)
    {
    	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述

    这个程序很巧,我们求一棵二叉树节点个数,就计算它左子树节点个数 + 右子树节点个数 + 1(根节点)

    2.8 计算二叉树叶子节点个数

    计算叶子节点个数和求节点个数的思想是一样的

    //求叶子节点个数
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

  • 相关阅读:
    idea如何导入maven项目
    let、var、const 的区别
    软考高项-计算题(3)
    【C++STL基础入门】list改、查操作
    互联网后端技术大全!
    【CloudCompare教程】001:CloudCompare中文版下载与安装图文教程
    UMI
    MyBatis简述
    【React】类excel表格的开源项目handsontable
    算法-KMP匹配
  • 原文地址:https://blog.csdn.net/qq_59702185/article/details/126386017