一、树和二叉树
(1)基本概述
1. 树的结点包含一个数据元素和若干指向其子树的分支(保存的是数据与数据之间的关系)。
2. 结点的度是指某个数据拥有的子树数(分支数)。叶子结点:度为0的结点,又称终端结点;度不为0的结点成为非终端结点或分支结点。
3.结点层次从根开始定义,根为第一层;树的结点的最大层次称为树的高度或深度。
4.二叉树:一种树形结构,特点是每个结点最多有两棵子树,它不存在度大于2的结点,且它的子树有左右之分,其次序不能任意颠倒。
5.二叉树的性质:
(1)第i层上最多有2^(i-1)个结点(i>=1);
(2)深度为k的二叉树最多有2^k-1个结点(k>=1);
(3)任何一棵二叉树T,其终端结点数为n0,度为2的结点数为n2,一定满足n0=n2+1;
满二叉树:一棵深度为k且有2^k-1个结点的二叉树称为满二叉树;在不改变树的深度的情况下,不能再往这棵树中添加任何的结点了。满二叉树属于完全二叉树。
完全二叉树:满足以下两个条件的二叉树称为完全二叉树:a.除去最后一层后的为满二叉树;b.最后一层的结点的排列左侧优先级高,也就是左边没有空洞。
(4)如果对一棵有n个结点的完全二叉树从上至下、从左至右,给每一个结点从1开始编号,那么当一个结点的编号为i时,它的父结点编号为i/2;它的左子结点编号为2*i;它的右子结点编号为2*i+1;
6.二叉树的保存:一是保存数据,二是保存数据与数据之间的关系
(1)顺序结构:用一组连续的存储单元保存二叉树中的每个结点以及结点与结点之间的关系。对于完全二叉树来说,用数组来保存完全可行,数组元素保存数据,数组下标保存结点与结点的关系。但是非完全二叉树不建议用顺序结构,因为可能会浪费太多的内存空间!!!
(2)链式结构:用数据域保存数据,指针域分别保存它的左子结点和右子结点的指针。
//数据的类型
typedef char TElemType;
//二叉树中结点的类型
typedef struct BiNode
{
//数据域
TElemType data;
//指针域
struct BiNode * lchild;
struct BiNode * rchild;
}BiNode;
7.如何去建立一棵二叉树呢?
一般用的比较多的是建立一棵二叉排序树。
(1)什么是二叉排序树:二叉排序树或者是一棵空树,或者是具有如下性质的二叉树:
a.若它的左子树不为空,则左子树上所有结点的值,均小于它的根结点的值;
b. 若它的右子树不为空,则右子树上所有结点的值,均大于它的根结点的值;
c.它的左子树和右子树也分别为二叉排序树。
(2)二叉排序树的创建过程,实际上就是把一个结点加入到一棵二叉排序树中去,使其加入之后,仍然要保持其排序性。
(3) 二叉排序树的插入操作:
a. 查找插入位置
新插入的结点一定是一个新添加的叶子结点,
并且是查找不成功时,查找路径上访问(比较)
的最后一个结点的 左子结点 或 右子结点。
b.插入操作
pf 是查找路径上的最后一个比较的结点的指针
p 是待插入结点的指针
if(p->data > pf->data)
pf->rchild = p;
else
pf->lchild = p;
8.二叉树的遍历:按照某条搜索路径访问树中的每个结点,使得每个结点有且仅被访问一次。
有三种遍历方式:
先序遍历(DLR):根 左 右
中序遍历(LDR):左 根 右
后序遍历(LRD):左 右 根
例:先序遍历:
void pro_order(BiNode * root) //pro_order : 按照先序遍历的方法来遍历root指向的二叉树
{
if(root == NULL)
{
return ;
}
printf("%c ", root->data);
pro_order(root->lchild);
pro_order(root->rchild);
}
9.平衡二叉树:又称AVL数,或者它是一棵空树,或者是具有如下性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树的高度与右子树的高度之差绝对值不超过1。