1. 树
1.1. 树(Tree)是n(n>=0)个结点的有限集。n=0时成为空树。
在任意有一颗非空树中:
- 有且仅有一个特定的结点被称为根的结点(结点都应包括两个部分:一个是需要用的实际数据data;另一个就是存储下一个结点地址的指针,即数据域和指针域。)
- 除根结点之外的其余数据元素被分为m(m≥0)个互不相交的集合T1,T2,……Tm-1,其中每一个集合Ti(1<=i<=m)本身也是一棵树,被称作原树的子树。(m>0时子树个数没有限制,但它们一定是互不相交的)
1.2. 结点拥有的子树数目称为结点的度。
某一结点的子树的根节点为该结点的孩子结点,该结点称为孩子结点的双亲结点。
上图中 结点A为结点B 结点C的双亲结点,B C为A的孩子结点,同一个双亲结点的孩子结点之间称为兄弟结点
没有孩子的结点(也就是度为0的结点)称为叶子(Leaf)或终端结点
至少有一个孩子的结点称为分支(Branch)或非终端结点。
1.3. 从根开始,根为第一层,根的孩子为第二层,依此类推
树中结点的最大层次数称为树的深度或高度。
2. 二叉树
二叉查找树 根结点的值大于左子树的任一结点的值,小于其 右子树任一结点的值
2.1. 二叉树:每个结点至多拥有两颗子树(即二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,顺序不能颠倒
2.2. 二叉树性质
- 在二叉树的第 i 层上最多有 2^(i-1)个结点。 (i>=1)
- 二叉树中如果深度为K,那么二叉树最多有2^(k) - 1个结点。(k>=1)
- n0 = n2 +1 n0表示度数为0的结点数,n2表示度数为2的结点数
- 完全二叉树中,具有n个结点的二叉树的深度为 [log2n]+1 ,其中[log2n]向下取整。
对二叉树进行编号,二叉树中任意一个编号为 i 的结点有如下特性:
- 若 i = 1,则该结点是二叉树的根,无双亲,否则编号为[ i / 2]的结点为其双亲结点;
- 若 2i > n,则该结点无左孩子,否则编号为 2i 的结点为其左孩子结点;
- 若 2i +1 > n,则该结点无有孩子结点,否则,编号为 2i + 1 的结点为其有孩子结点;
2.3. 完美二叉树(Perfect Binary Tree)
一个深度为k(k >= -1) 且有 2^(k+1) -1 个结点的二叉树称为完美二叉树。(满二叉树)
2.4. 完全二叉树 (Complete Binary Tree)
完全二叉树从根结点到倒数第二层满足完美二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐。
右图中如果将编号11(k)结点从编号6(F)的左儿子位置移动到编号5(E)的右儿子位置,则变成一棵完全二叉树。
2.5. 完满二叉树
所有非叶子结点的度都是2 (只要有孩子结点,就必然有两个孩子结点)
2.6. 完美二叉树、完全二叉树、完满二叉树等价关系
- 完美二叉树一定是完全二叉树,但完全二叉树不一定是完美二叉树;
- 完美二叉树一定是完满二叉树,但完满二叉树不一定是完美二叉树;
- 完全二叉树可能是完美二叉树,完满二叉树也可能是完全二叉树;
- 既是完全二叉树又是完满二叉树也不一定是完美二叉树;
2.7. 斜树
斜树:所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。
2.8. 二叉树遍历
已知中序遍历和后序遍历,可确定一棵二叉树。后序遍历最后一个结点为树的根结点
已知前序遍历和后序遍历序列,不可以唯一确定一棵二叉树。
先序遍历,从二叉树的根节点出发,第一次到达结点时就输出数据。按照从左到右的方向访问。
从根结点出发,则第一次到达结点A,故输出A;
继续向左访问,第一次访问结点B,故输出B;
按照同样规则,输出D,输出H;
当到达叶子结点H,返回到D,此时已经是第二次到达D,故不在输出D,进而向D右子树访问,D右子树不为空,则访问至I,第一次到达I,则输出I;
I为叶子结点,则返回到D,D左右子树已经访问完毕,则返回到B,进而到B右子树,第一次到达E,故输出E;
向E左子树,故输出J;
按照同样的访问规则,继续输出C、F、G;
遍历结果为:A B D H I E J C F G
/*二叉树的前序遍历递归算法*/
public void PreOrderTraverse(BiTree T)
{
if(T==NULL)
return;
print("结点:" + T->data); /*显示结点数据
PreOrderTraverse(T->lchild); /*再先序遍历左子树*/
PreOrderTraverse(T->rchild); /*最后先序遍历右子树*/
}
中序遍历(先输出左孩子结点):从二叉树根结点出发,当第二次(判断其左孩子是否存在,不存在则输出,存在则继续向下进行这一步)到达结点时就输出结点数据,按照先向左(子树)再向右(子树)的方向访问。
从根结点出发,则第一次到达结点A,不输出A,继续向左访问,第一次访问结点B,不输出B;继续到达D,H;
到达H,H左子树为空,则返回到H,此时第二次访问H,故输出H;
H右子树为空,则返回至D,此时第二次到达D,故输出D;
由D返回至B,第二次到达B,故输出B;
按照同样规则继续访问,输出J、E、A、F、C、G;
遍历结果为:H D I B J E A F C G
/*二叉树的中序遍历递归算法*/
public void InOrderTraverse(BiTree T)
{
if(T==NULL)
return;
InOrderTraverse(T->lchild); /*中序遍历左子树*/
print("结点:" + T->data); /*显示结点数据
InOrderTraverse(T->rchild); /*最后中序遍历右子树*/
}
后序遍历:从二叉树根结点出发,当第三次(判断其右孩子结点是否存在)到达结点时输出结点数据,按照先向左(子树)再向右(子树)的方向访问。
从根结点出发,则第一次到达结点A,不输出A,继续向左访问,第一次访问结点B,不输出B;继续到达D,H;
到达H,H左子树为空,则返回到H,此时第二次访问H,不输出H;
H右子树为空,则返回至H,此时第三次到达H,故输出H;
由H返回至D,第二次到达D,不输出D;
继续访问至I,I左右子树均为空,故第三次访问I时,输出I;
返回至D,此时第三次到达D,故输出D;
按照同样规则继续访问,输出J、E、B、F、G、C,A;
遍历结果为:H I D J E B F G C A
/*二叉树的后序遍历递归算法*/
public void PostOrderTraverse(BiTree T)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild); /*先后序遍历左子树*/
PostOrderTraverse(T->rchild); /*再后续遍历右子树*/
print("结点:" + T->data); /*显示结点数据
}
2. 二叉链表
链表结构存储二叉树,这种链表称为二叉链表
# 定义结点代码
typedef struct BiTNode{
TElemType data;//数据
struct BiTNode *lchild, *rchild;//左右孩子指针
} BiTNode, *BiTree;