树
- 树是一种非线性结构
树的定义
- 有且只有一个称为根的节点
- 有若干个互不相交的子树,这些子树本身也是一颗树
- 深度:从根节点到最底层节点的层数称之为深度 根节点是第一层
- 叶子节点:没有子节点的节点
- 非终端节点:实际就是非叶子节点
- 度 :子节点的个数称为度
树的分类
-
一般树:任何一个节点的子节点的个数都不受限制
-
二叉树:任意一个子节点的个数最多两个,且子节点的位置不可更改
1.满二叉树:在不增加树的层数的前提下,无法再多添加一个节点的二叉树就是满二叉树
2.完全二叉树:如果只是删除了满二叉树最底层最右边的连续若干个节点,这样形成的二叉树就是完全二叉树
-
森林:n个互不相交的树的集合
树的存储
二叉树的存储
连续存储(完全二叉树)
- 优点:查找某个节点的父节点和子节点的速度很快
- 缺点:耗用内存空间较大
链式存储
- 结构体有两个指针域,一个指向它的左儿子,一个指向它的右儿子,如果没有值则赋为NULL
一般树的存储
- 双亲表示法:在每个节点中 附设一个指示器指示其双亲结点到链表中的位置
//树的双亲表示法的结构体定义
#define MAX 100
typedef struct PTNode{
int data;//结点数据
int parent;//双亲位置
}PTNode;
typedef struct{ //树结构
PTNode nodes[MAX];//结点数组
int r,n;//根的位置和结点数
}PTree;
- 孩子表示法:每个结点有多个指针域,其中每个指针指向一棵子树的根结点,我们把这种方法叫做多重链表表示法
具体办法:把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果叶子结点则此单链表为空,然后n个头指针又成一个线性表,采用顺序存储结构,存放进一个一维数组中
//树的孩子表示法的结构体定义
#define MAX 100
typedef struct CTNode{ //孩子结点
int child;
int CTNode *next;
}*ChildPtr;
typedef struct{ //表头结构
int data;
ChildPtr firstchild;
}CTBox;
typedef struct{ //树结构
CTBox nodes[MAX];//结点数组
int r,n;//根的位置和结点数
}CTBox;
- 孩子双亲表示法:前两种表示方法的结合,在表头结构中在加入一个双亲结点的结构体变量
- 二叉树表示法:把一个普通的树转化成二叉树来存储,具体转换方法:左指针域指向它的第一个孩子,右指针域指向他的下一个兄弟 一个普通的树转化成的二叉树一定没有右子树
二叉树的定义与性质
- 定义:二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成
- 性质
1.在二叉树的第i层上至多有2的i-1次方个结点
2.深度为k的二叉树至多有2的k次方-1个结点
3.对任何一颗二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。另外,如果n1为度是1的结点数,那么树T的总结点数n=n0+n1+n2
4.具有n个结点的完全二叉树的深度为log以2为底的n的对数+1
5.对于一颗有n个结点的完全二叉树的结点按层序编号,如果i=1,则为二叉树的根,双亲是[i/2],左节点是[i2],右节点是[i2+1].
二叉树的存储
1.顺序存储结构
- 完全二叉树:用一组地址连续的存储单元 从上而下 从左到右(按层遍历) 存储完全二叉树的节点
- 非完全二叉树:用一组地址连续的存储单元 从上而下 从左到右 (按层遍历)存储二叉树的节点;用“0”表示相应位置上不存在的节点
2.链式存储结构
- 二叉链表节点 (lchild , data , rchild)、 三叉链表节点 (lchikd , data , parent , rchild) 链表的头指针指向根结点;
- 在含有n个节点的二叉链表中 有n+1个空链域
二叉树的遍历
二叉树的遍历方法主要分为三种,前序遍历,中序遍历,后续遍历
1.先序遍历[先访问根节点]
… … …先访问根结点
… … …再先序访问左子树
… … …再先序访问右子树
//二叉树的前序遍历递归算法
void PreOrderTraverse(BiTree T){
if(T==NULL)
return;
printf("%c",T->data);//显示结点数据 可更改为其他操作
PreOrderTraverse(T->lchild);//先先序遍历左子树
PreOrderTraverse(T->rchild);//最后先序遍历右子树
}
2.中序遍历[中间访问根节点]
… … …先中序遍历左子树
… … …再访问根结点
… … …再中序遍历右子树
//二叉树的中序遍历递归算法
void InOrderTraverse(BiTree T){
if(T==NULL)
return;
InOrderTraverse(T->lchild);
printf("%c",T->data);
InOrderTraverse(T->rchild);
}
3.后序遍历[中间访问根节点]
… … …先中序遍历左子树
… … …再中序遍历右子树
… … …再访问根结点
//二叉树的后序遍历递归算法
void PostOrderTraverse(BiTree T){
if(T==NULL)
return;
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%c",T->data);
}
注意:
已知数的先序遍历和后序遍历不能唯一的确定一颗树。
先序遍历+中序遍历=唯一确定一棵树;
后序遍历+中序遍历=唯一确定一颗树;
树、森林与二叉树的转换
- 树转换为二叉树
左儿子是它的第一个孩子,右儿子是他的一个兄弟 - 森林转换为二叉树
森林中的每一棵树都是兄弟,当成右孩子处理