树和二叉树
基本术语
结点:树的数据元素;
结点的度:结点上分支出的子树个数;一个树中最大的结点的度称为树的度。
叶子:度为0的结点,又称为终端结点
孩子:结点的子树的根,称为该结点的孩子
双亲:对应于孩子结点的上层结点称为这个结点的双亲
兄弟:同一双亲的孩子之间互称为兄弟
结点的层次:从根结点算起,根为第一层,其他结点的层次等于它的双亲的层次加1
深度:或称为高度,是树中结点的最大层次,
森林:是n(大于等于0)棵互不相交的树的集合。
树
树(Tree)是一种由多个节点组成的有限集合T,有且仅有一个节点称为根(root),其余结点分为m(大于等于0)个互不相交的有限集合T1,T2,T3...;每个集合本身又是棵树,被称为这个根的子树。
在树的定义中规定了树含有结点数必须大于0,这表明空集不可以称为树;他又规定结点可以为1,该结点就是根节点。
树的存储结构
1. 结点异构型
根据结点的子树数设置相应的指针域,同时在结点的数据中再增加一个表示结点的度的域,以了解这个结点具有的指针域。
2. 结点同构型
以这棵树的度作为结点的指针域数目,每个结点的长度一样,但是树中很多结点的度小于树的度,导致存储浪费。
二叉树
二叉树是n(大于等于0)个结点的有限集合,它或是空树,或是有一个根节点,以及两棵互不相交的,分别称为左子树和右子树的二叉树组成。
满二叉树
在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
满二叉树的特点有:
- 叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
- 非叶子结点的度一定是2。
- 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多
完全二叉树
对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
二叉树的基本性质
1. 二叉树的第i层至多有2^(n-1)个结点;
2. 深度为k的二叉树至多有2^k - 1个结点
3. 对任意一颗二叉树,若2度结点数为N2 ,则叶子数为 N0 = N2 + 1;
完全二叉树的重要性质:
4. 在完全二叉树中,具有n个节点的完全二叉树的深度为[log2n]+1,其中[x]是向下取整。
5. 若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:
(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。
二叉树和树的区别
二叉树不是树的特殊情况,尽管树和二叉树之间有许多关系,但是他们是两个概念,区别如下:
- 不包含任何结点的空树也是一颗二叉树,和树的定义不一样,树的结点必须大于0
- 二叉树的子树是有序的,他有左子树和右子树之分,且不能随意颠倒
- 二叉树至多有两个孩子
- 二叉树中某结点即使只有一棵子树,也要区分它是左子树还是右子树
二叉树的存储结构
1. 顺序存储结构
二叉树的顺序存储结构就是使用一维数组存储二叉树中的结点,并且结点的存储位置,就是数组的下标索引。
a. 完全二叉树的顺序存储
b. 非完全二叉树的顺序存储
2. 链式存储结构
常用的链式存储为二叉链表,链表中的每个结点为:
二叉链表的示意图:
二叉树的遍历
typedef struct node{
int data;
stuct node* lchild;
stuct node* rchild;
}NODE;
1. 先序遍历
先访问根节点,后先序左子树,再先序右子树
上图的输出:A ----> B ----> D ------> E -------> C
算法:
/*二叉树的先序遍历递归算法*/ void PreOrderTraverse(NODE* T) { if(T==NULL) return; printf("%c", T->data); /*显示结点数据,可以更改为其他对结点操作*/ PreOrderTraverse(T->lchild); /*再先序遍历左子树*/ PreOrderTraverse(T->rchild); /*最后先序遍历右子树*/ }
2. 中序遍历
先中序遍历左子树,后访问根节点,再中序遍历右子树
上图的输出:D ----> B ----> E ------> A -------> C
算法:
/*二叉树的中序遍历递归算法*/ void InOrderTraverse(NODE* T) { if(T==NULL) return; InOrderTraverse(T->lchild); /*中序遍历左子树*/ printf("%c", T->data); /*显示结点数据,可以更改为其他对结点操作*/ InOrderTraverse(T->rchild); /*最后中序遍历右子树*/ }
3. 后序遍历
先后序遍历左子树,然后后序遍历右子树,再访问根节点
上图输出:D ----> E ----> B ------> C -------> A
算法:
/*二叉树的后序遍历递归算法*/ void PostOrderTraverse(NODE* T) { if(T==NULL) return; PostOrderTraverse(T->lchild); /*先后序遍历左子树*/ PostOrderTraverse(T->rchild); /*再后续遍历右子树*/ printf("%c", T->data); /*显示结点数据,可以更改为其他对结点操作*/ }