提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
二叉树
前言
本章介绍数据结构二叉树。
一、树是什么?
在了解二叉树之前,先来大体的了解了解树这个概念。
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。
树之所以称之为树,是因为树这种结构在逻辑上长得很像一颗倒挂的树,也就是说它的根是朝上的,叶子是朝下的。
- 有一个特殊的结点,称之为根,根节点没有前驱结点,是树的根本。
- 除根结点外,其余结点被分成多个互不相交的集合,每个集合都可看作是一个与树结构类似的子树,每棵子树有且仅有一个前驱结点,但可以有多个或零个后继结点。
- 树是递归定义的。
注意:
子树之间是不能有交集的,也就是树之中不能形成环,这样就是另外一种数据结构了。
- 子树不能有交集。
- 除了根节点外,每个结点有且仅有一个前驱结点,多个或零个后继结点。
- 有N个结点,就有N-1条边
二、树
1、概念
- 结点的度:一个结点含有的子树的个数称为结点的度。(上图中1的度为3)
- 叶子结点:度为0的结点称为叶子结点,即该节点没有后继结点。(上图中3、4、5、6为叶子结点)
- 父结点:若一个结点含有子节点,则称该结点为其所有子节点的父节点。(上图中1为2、5、7的父节点,2为3、4的父节点)
- 子结点:一个结点含有的子树的根结点称为该结点的子结点。(上图中2、5、7是1的子节点,6是7的子节点)
- 兄弟结点:
- 拥有相同父结点的结点称之为兄弟结点。(上图中2、5、7为兄弟结点)
- 树的度:一棵树中,最大的结点的度称之为树的度。(上图中的树的度为3,因为最大的结点的度是1的度3)
- 树的深度:简单来说就是有多少层深度就是多少。(上图中有3层所以深度是3)
- 森林:由多棵互不相交的树组成的集合称为森林。
2、应用
我们可以看到的树在实际中运用到的场景就是这个文件系统的目录树结构,其中此电脑就相当于根,此电脑下有很多文件夹和文件,也就相当于子树了。
三、二叉树
1、概念
二叉树的每个结点(除了根节点)有且仅有一个前驱结点,并且每个结点只能有最多两个子节点,最少0个子节点。
- 二叉树不存在度大于2的结点。
- 二叉树的子树有左右之分,因此子树的次序不能颠倒。
2、特殊的二叉树
1)、满二叉树
一棵二叉树,如果每一层的结点个数到达到了最大值,那么就说明这是一棵满二叉树,如果一棵二叉树的层数为N,结点总个数满足2^N-1,那么它就是一棵满二叉树。
2)、完全二叉树
一棵二叉树,除了最后一层以外其余层都是满的,并且最后一层的结点从左到右是依此连续的,称之为完全二叉树,满二叉树是特殊的完全二叉树。
3、性质
- 一棵非空二叉树的第n层最多有2^(n-1)个结点。
- 一棵深度为n的二叉树的最大结点数为2^n-1个。
- 对于任何一棵二叉树,都有度为0的结点的个数等于度为2的结点的个数加一。
- 具有n个结点的满二叉树的深度h=log2(n+1)(log以2为底n+1的对数)。
四、链式二叉树
二叉树的链式结构指的是,像链条一样把二叉树的各个结点链接在一起。通常的方法是每个结点由三个域组成,数据域和左右指针域,左右指针域通常存储该结点左子树和右子树的结点地址。
1、结点的定义
一个结点有三个域组成:
- 数据域(val):存储当前结点的值
- 左子树指针域(left):存储当前结点的左子树的地址
- 右子树指针域(right):存储当前结点的右子树的地址
代码如下:
//便于后续数据类型的修改
typedef char BTDataType;
//二叉树的结点
typedef struct BinaryTreeNode
{
BTDataType val;//数据域
struct BinaryTreeNode* left;//左孩子
struct BinaryTreeNode* right;//右孩子
}BTNode;
2、前序遍历
前序遍历,即按根、左子树、右子树的顺序遍历一棵树。
代码如下:
// 二叉树前序遍历(根、左、右)
void BinaryTreePrevOrder(BTNode* root)
{
//递归到空时直接返回
if (!root)
{
return;
}
//直接取当前根的值
printf("%d ", root->data);
//递归左子树
BinaryTreePrevOrder(root->left);
//递归右子树
BinaryTreePrevOrder(root->right);
}
3、中序遍历
中序遍历,即按左子树、根、右子树的顺序遍历一棵树。
代码如下:
// 二叉树中序遍历(左、根、右)
void BinaryTreeInOrder(BTNode* root)
{
//递归到空时直接返回
if (!root)
{
return;
}
//递归左子树
BinaryTreeInOrder(root->left);
//取根的值
printf("%d ", root->data);
//递归右子树
BinaryTreeInOrder(root->right);
}
4、后序遍历
后序遍历,即按左子树、右子树、根的顺序遍历一棵树。
代码如下:
// 二叉树后序遍历(左、右、根)
void BinaryTreePostOrder(BTNode* root)
{
//递归到空时直接返回
if (!root)
{
return;
}
//递归左子树
BinaryTreePostOrder(root->left);
//递归右子树
BinaryTreePostOrder(root->right);
//取根的值
printf("%d ", root->data);
}
5、求二叉树的结点个数
代码如下:
// 二叉树结点个数
int BinaryTreeSize(BTNode* root)
{
//空结点返回0
if (!root)
return 0;
//返回左边结点个数+右边结点个数+本身个数
return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
注意递归到空时返回的是0,当一个结点左右子树都为空时,返回两个0,关键的是需要加上1,因为自己本身要算进结点个数里。
6、求二叉树的叶子结点个数
代码如下:
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
//根节点为空表示这是一个空树
if (!root)
return 0;
//左、右为空为叶子结点
if (!root->left && !root->right)
return 1;
//返回左、右两个叶子的个数
return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
总结
温故而知新。