目录
1. 树的相关知识
1.1 树的的定义
树是一种非线性的数据结构(线性的数据结构:顺序表,链表,栈,队列),它是由n(n>=0)个有限结点,组成一个具有层次关系的集合。之所以称其为树,原因是它像一颗倒挂的树。
1.2 数的特性
·树有一个特殊的结点,称其为根结点,根结点木有前驱结点。
·除去根结点外,其余的结点被分为M(M>0)个不相交的集合,其中的每一个集合又是一颗结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。
·因此树是由递归定义的。
1.3 树的相关概念
节点的度:一个节点含有的子树的个数称为该节点的度。如上图:A的为6。
叶节点或终端节点:度为0的节点称为叶节点;如上图:B、C、H、I...等节点为叶节点。
非终端节点或分支节点:度不为0的节点;如上图:D、E、F、G...等节点为分支节点。
双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;如上图:A是B的父节点 。孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;如上图:B是A的孩子节点。
兄弟节点:具有相同父节点的节点互称为兄弟节点;如上图:B、C是兄弟节点。
树的度:一棵树中,最大的节点的度称为树的度;如上图:树的度为6。
节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推。树的高度或深度:树中节点的最大层次;如上图:树的高度为4。
节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先。
子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
森林:由m(m>0)棵互不相交的多颗树的集合称为森林。(数据结构中的学习并查集本质就是一个森林)
注:还有一种说法认为节点的层次和树的深度是从0开始数的,那么当树为空树时其深度则为-1。
1.4 树的常见表示法
1.4.1 孩子兄弟表示法
一个数据域用来存储数据。两个指针域:一个用来指向他的孩子,另一个用来指向他的兄弟。没有孩子或兄弟指向空即可。
1.4.2 双亲表示法
将节点信息存储在数组中,值指向双亲的下标,以此来表示各节点之间的关系。
R的值为-1,不是一个有效的下标,代表其为根结点。
A的值为0,代表A的双亲是下标为0的节点。
F的值为3,代表F的双亲是下标为3的节点。
以此类推······
2. 二叉树的相关知识
2.1 二叉树的概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结点加上两棵别称为左子树和右子树的二叉树组成。
2.2 二叉树的特点
1. 每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
2. 二叉树的子树有左右之分,并且顺序不能颠倒。
根据二叉树的特点,对于任何一个二叉树,都可以看成三部分:根结点,左子树,右子树。
2.3 二叉树的前中后序
在讲解该问题之前,我们得先了解一下分治算法,分治:分而治之,将大问题分成类似的子问题,子问题再分成子问题······,直到子问题不可被分割。
例如对于上图中的二叉树,还可以进行如下分割:将B看作根结点,D为左子树,E为右子树。
将D看作根结点,他的左右子树都为NULL,即分割到此后不可再次分割。将E和C看作根结点同理。
为什么树要分前中后序?
树的遍历十分讲究,它的遍历不同于链表(链表要么从后往前遍历,要么从前往后遍历),后续求树的节点个数,最大深度等都可能会涉及到二叉树的遍历。既然这样树的遍历分割为前序,中序, 和后序。
前序:遍历二叉树时先是根结点,再是左子树,最后是右子树。上图二叉树的前序为:A B D NULL NULL E NULL NULL C NULL NULL 。(不写NULL也行,但写NULL更能解释其本质)
就是用分治算法来分析得到结果的。A为根结点,A的左下为A的左子树,A的右下为A的右子树。
对A左子树进行同样的操作,B为根结点,B的左下为B的左子树,B的右下为B的右子树。以此类推,根据前序的特点,就可写出该二叉树的前序。
中序:遍历二叉树时先是左子树,再是根结点,最后是右子树。原理同上,中序:NULL D NULL B NULL E NULL A NULL C NULL。
后序:遍历二叉树时先是左子树,再是右子树,最后是根结点。这个就交给你们咯。
2.4 前中后序的代码实现
2.4.1 快速构建二叉树
定义一个结构体类型,成员包括一个存放数据的变量,一个指向左边的孩子的结构体指针,一个指向右边的孩子的结构体指针。通过动态开辟内存的方式创建结点,最后再进行结点之间的连接。
typedef char B_T_DATA_TYPE;
typedef struct BinaryTreeNode BTNode;
struct BinaryTreeNode
{
B_T_DATA_TYPE data;
//根结点的左孩子
struct BinaryTreeNode* left;
//根结点的右孩子
struct BinaryTreeNode* right;
};
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 = 'A';
E->left = NULL;
E->right = NULL;
//连接结点
A->left = B;
A->right = C;
B->left = D;
B->right = E;
2.4.2 通过递归来书写前序
void PrevOrder(BTNode* root)
{
//递归结束条件
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->data);
//左子树
PrevOrder(root->left);
//右子树
PrevOrder(root->right);
}
2.4.3 完整代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef char B_T_DATA_TYPE;
typedef struct BinaryTreeNode BTNode;
struct BinaryTreeNode
{
B_T_DATA_TYPE data;
//根结点的左孩子
struct BinaryTreeNode* left;
//根结点的右孩子
struct BinaryTreeNode* right;
};
void PrevOrder(BTNode* root)
{
//递归结束条件
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->data);
//左子树
PrevOrder(root->left);
//右子树
PrevOrder(root->right);
}
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 = 'A';
E->left = NULL;
E->right = NULL;
//连接结点
A->left = B;
A->right = C;
B->left = D;
B->right = E;
PrevOrder(A);
printf("\n");
return 0;
}
中序以及后序只需要改变递归时代码的顺序即可。