模块二:非线性结构----树
1.定义
专业定义:
1、有且只有一个称为根的结点。2、有若干个互不相交的子树,这些子树本身也是一个树。
通俗说法:
1、树是由结点和边组成。
2、每个结点只有一个父节点,但可以有多个子节点。
3、但有一个结点例外,该结点没有父节点,此节点称为根节点。
专业术语:
节点 父节点 子节点 子孙 堂兄弟深度:
从根节点到最底层节点的层数 称之为深度
根节点是第一层。
叶子节点:
没有子节点的节点
非终端结点:
实际就是非叶子节点。(有子节点)
度:
子节点的个数称为度。(几个孩子)
树的度:
结点的最多孩子
2.分类
一般树任意一个节点的子节点的个数都不受控制
二叉树
任意一个节点的子节点个数最多有两个,且子节点的位置不可变。
左子树就是左子树,右子树就是右子树 。二叉树是一个有序树。
二叉树分类:
① 一般二叉树
② 满二叉树
③ 完全二叉树
如果只是删除了满二叉树最底层最右边的连续若干个节点,
这样形成的二叉树,就是完全二叉树。(满二叉树是完全二叉树的一个特例(一个都不删)
森林
N个互不相交的树的集合。
3. 树的存储
3.1、二叉树的存储
连续存储 [ 完全二叉树 ]一般二叉树要以数组的方式存储,要先转化成完全二叉树,因为如果只存有效节点(无论以哪种规则:先序,中序,后序),
则无法知道这个树的原本样子。
缺点:耗用内存空间过大
链式存储
数据域 指针域【 左指针(左孩子) 右指针(右孩子)】
N个结点只浪费N+1个指针空间(空指针);
3.2、一般树的存储
① 双亲表示法
求父节点方便
② 孩子表示法
求子节点方便
③ 双亲孩子表示法
求父节点和子节点都很方便。
④ 二叉树表示法(常用)
把一个普通树转换成二叉树来存储。
设法保证任意一个节点的
左指针域指向它的第一个孩子
右指针域指向它的下一个兄弟
只要能满足此条件,就可以将一个普通树转换成二叉树
一棵普通树转化成二叉树,根节点肯定没有右子树。
⑤森林的存储先把森林转化为二叉树,再存储二叉树
4.二叉树操作
4.1 、遍历
① 先序遍历(1) 先访问 根节点
(2) 先序遍历左子树
(3) 先序遍历右子树
(1)中序遍历左子树
(2)访问 根节点
(3)中序遍历右子树
(1)后序遍历左子树
(2)后续遍历右子树
(3)访问根节点。
4.2、已知两种遍历序列求原始二叉树
(1)先序+中序
(2)后序+中序
5.树的应用
树是数据库中数据组织的一种重要形式。操作系统子父进程关系的本身就是一个树
面向对象语言中 类的继承关系本身就是一棵树。
赫夫曼树: 一个实物有N种取值,每种取值的概率是不一样的.如何求最优
实例: 实现二叉树 先序 中序 后序
#include<stdio.h>
#include<malloc.h>
struct BTNode{
char data;
struct BTNode * pLchild; //p是指针 L是左 child是孩子
struct BTNode * pRchild;
};
struct BTNode * CreateBTree(void);
void PreTraverseBTree(struct BTNode* pT);
void InTraverseBTree(struct BTNode* pT);
void PostTraverseBTree(struct BTNode* pT);
int main(void){
struct BTNode * pT = CreateBTree();
PreTraverseBTree(pT);
printf("\n");
InTraverseBTree(pT);
printf("\n");
PostTraverseBTree(pT);
return 0;
}
//先序遍历 根左右
void PreTraverseBTree(struct BTNode* pT){//根左右
if(NULL != pT){
printf("%c",pT->data);
if(NULL != pT->pLchild){
PreTraverseBTree(pT->pLchild);
}
if(NULL != pT->pRchild){
PreTraverseBTree(pT->pRchild);
//pT->pLchild 可以代表整个左子树
}
}
}
//中序遍历 左根右
void InTraverseBTree(struct BTNode* pT){//左根右
if(NULL != pT){
if(NULL != pT->pLchild){
InTraverseBTree(pT->pLchild);
}
printf("%c",pT->data);
if(NULL != pT->pRchild){
InTraverseBTree(pT->pRchild);
//pT->pLchild 可以代表整个左子树
}
}
}
//后续遍历 左右根
void PostTraverseBTree(struct BTNode* pT){
if(NULL != pT){
if(NULL != pT->pLchild){
PostTraverseBTree(pT->pLchild);
}
if(NULL != pT->pRchild){
PostTraverseBTree(pT->pRchild);
//pT->pLchild 可以代表整个左子树
}
printf("%c",pT->data);
}
}
//创建一颗树
struct BTNode * CreateBTree(){
struct BTNode* pA = (struct BTNode *)malloc(sizeof(struct BTNode));
struct BTNode* pB = (struct BTNode *)malloc(sizeof(struct BTNode));
struct BTNode* pC = (struct BTNode *)malloc(sizeof(struct BTNode));
struct BTNode* pD = (struct BTNode *)malloc(sizeof(struct BTNode));
struct BTNode* pE = (struct BTNode *)malloc(sizeof(struct BTNode));
pA->data = 'A';
pB->data = 'B';
pC->data = 'C';
pD->data = 'D';
pE->data = 'E';
pA->pLchild = pB;
pA->pRchild = pC;
pB->pLchild = pB->pRchild = NULL;
pC->pLchild = pD;
pC->pRchild = NULL;
pD->pLchild = NULL;
pD->pRchild = pE;
pE->pLchild = pE->pRchild = NULL;
return pA;
}