目录
前言
在我们学习树和二叉树之前,我们已经学习了数据结构当中的几种线性数据结构:顺序表、链表、栈和队列等,而树和二叉树是数据结构当中的另一大类——非线性数据结构。

一.树
1.1树的定义
树是一种非线性的数据结构,它是由n(n>=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)节点的层次:从根节点开始定义起,根为第一层,根的子节点为第二层,以此类推;
(9)树的高度或深度:树中节点的最大层次;如上图,树的高度是4;
(10)节点的祖先:从根节点到该节点所经分支上的所有节点;如上图:A是所有节点的祖先;
(11)子孙:以某节点为根的子树中任意一个节点称为该节点的子孙。如上图:所有节点都是A的子孙;
(12)森林:由m(m>0)棵互不相交的多棵树的集合称为森林;(数据结构中的学习并查集本质就是一个森林);
1.3树与非树
树有以下特征:
(1)树的子树互不相交;
(2)除了根节点以外,每个节点有且仅有一个父节点;
(3)具有N个节点的树共有N-1条边;
非树:
树:

二.二叉树
2.1二叉树的定义
二叉树其实是一棵特殊的树,一棵二叉树是结点的有限集合,该集合或者为空,或者是由一个根节点加上两棵分别称为左子树和右子树的二叉树组成。
2.2二叉树的特点
二叉树有以下特点:
(1)二叉树中不存在度大于2的节点,每个节点最多有两棵子树;
(2)二叉树的子树有左右之分,且次序不能颠倒;
数据结构中的二叉树如下图所示:

2.3两种特殊的二叉树
接下来介绍两种特殊的二叉树:满二叉树和完全二叉树。
2.3.1满二叉树

如图所示是一棵满二叉树,从图中我们不难看出,满二叉树,顾名思义,就是二叉树的每一层的节点都是满的,即每一层的节点都达到了最大值。
满二叉树有以下特性:

2.3.2完全二叉树

如图所示,一棵完全二叉树的特点是:该二叉树的前(h-1)层是满的,而最后一层不满,但是连续的。
需注意,完全二叉树中度为1的节点的个数为0或1,这条性质在二叉树的性质相关题目时会用到。
2.4二叉树的性质
一棵二叉树具有以下性质:
(1)若规定根节点的层数是1,则一棵非空二叉树的第k层上最多有2^(k-1)个节点;
(2)若规定根节点的层数是1,则深度为h的二叉树最多有2^h-1个节点;
(3)对任何一棵二叉树,如果度为0的节点的个数计为n0,度为2的节点的个数计为n2,则有n0 = n2 + 1;
(4)若规定根节点的层数是1,具有n个节点的满二叉树的深度:h = log(N+1);
2.5二叉树相关OJ题的实现
在用代码实现二叉树的相关操作之前,我们需要对二叉树有一定的认识。
首先,我们需要将任何一棵二叉树看成由以下三个部分所组成:根节点,左子树,右子树。
其次,二叉树的相关习题中用到次数对多的思想是——递归,因为二叉树是递归定义的:一棵二叉树可以看成是根节点,左子树和右子树,而左子树和右子树也是二叉树,也可以拆分成根节点,左子树和右子树,如此递归下去,所以,在用代码实现二叉树之前,我们需要对递归有一定的理解。
2.5.1二叉树结点的定义
我们以链式结构的形式来实现二叉树,二叉树结点定义的相关代码如下:
//二叉树的定义
typedef BTNode* BTDataType;
struct BinaryTreeNode
{
BTDataType data;//数据域
struct BinaryTreeNode* left;//左孩子指针
struct BinaryTreeNode* right;//右孩子指针
};
2.5.2二叉树的遍历
2.5.2.1前序遍历
二叉树的前序遍历是:先访问根节点,再访问左子树,再访问右子树。代码实现如下所示:
//二叉树的前序遍历
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
printf("%c ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
2.5.2.2中序遍历
二叉树中序遍历是:先访问左子树,再访问根节点,再访问右子树。代码实现如下所示:
//二叉树的中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
2.5.2.3后序遍历
二叉树的后序遍历是:先访问左子树,再访问右子树,最后访问根节点。代码实现如下所示:
//二叉树的后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%c ", root->data);
}
二叉树的前、中、后序遍历统称为深度优先遍历。
2.5.3二叉树的层序遍历
二叉树的层序遍历,也被称为广度优先遍历,层序遍历,顾名思义,就是一层一层地对二叉树进行遍历,层序遍历的思想即实现如下所示:

二叉树的层序遍历代码实现如下所示:
//二叉树的层序遍历
void LevelOrder(BTNode* root)
{
//定义队列
Queue pq;
//对队列进行初始化
QueueInit(&pq);
//判断数是否为空
if (root == NULL)
{
return;
}
else
{
//树的根节点入队
QueuePush(&pq, root);
}
while (!QueueEmpty(&pq))
{
//出队头元素
BTNode* front = QueueHead(&pq);
QueuePop(&pq);
printf("%c ", front->data);
//将队头节点的左右孩子入队
if (front->left != NULL)
{
QueuePush(&pq, front->left);
}
if (front->right != NULL)
{
QueuePush(&pq, front->right);
}
}
QueueDestory(&pq);
}
2.5.4求二叉树中结点的个数
求二叉树中结点个数的代码如下所示:
//求二叉树的节点的个数
int TreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return 1 + TreeSize(root->left) + TreeSize(root->right);
}
2.5.5求二叉树中叶子节点的个数
求二叉树中叶子结点的个数的代码如下所示:
//求二叉树的叶子节点的个数
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);
}
2.5.6求二叉树的高度
求二叉树的高度的代码如下所示:
//求二叉树的最大高度或深度
int maxDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int left = maxDepth(root->left);
int right = maxDepth(root->right);
return left >= right ? 1 + left : 1 + right;
}
2.5.7判断一棵二叉树是否是平衡二叉树
判断一棵二叉树是否是平衡二叉树的代码如下所示:
//判断一颗二叉树是否为平衡二叉树
bool isBalanced(BTNode* root)
{
if (root == NULL)
{
return true;
}
int leftdepth = maxDepth(root->left);
int rightdepth = maxDepth(root->right);
return abs(leftdepth - rightdepth) < 2 && isBalanced(root->left) && isBalanced(root->right);
}
总结
树和二叉树是数据结构中非常重要的一部分内容,在后续学习排序等算法时也会用到树和二叉树的相关知识,希望本篇博客对大家学习二叉树有所帮助!





