1.基本概念
树是一种非线性结构,其严格的数学定义是:如果一组数据中除了第一个节点(第一个节点称为根节点,没有直接前驱节点)之外,其余任意节点有且仅有一个直接前驱,有零个或多个直接后继,这样的一组数据形成一棵树。这种特性简称为一对多的逻辑关系。
2.常见例子:
日常生活中,很多树的例子,比如一个公司的职员层级关系,学校中院系层级关系,族谱关系等,由于树状结构表现出来都是具有层次的,因此也称为层次结构。
通常,在逻辑上表达一棵抽象的树状结构的时候,习惯于将树根放在顶部,树枝树杈向下生长,如下图所示。
3.树的基本术语
若一个节点有子树,那么该节点称为子树根节点的“双亲“,子树的跟是该节点的“孩子”。有相同双亲的节点互为“兄弟节点”。一个节点的所有子树上的任何节点都是该节点的后裔。从根节点到某个节点的路径上的所有节点都是该节点的祖先。
节点的度:节点拥有的子树的数目。
叶子:度为零的节点。
分支节点:度不为零的节点。如上图K,L,G,M,I为叶子
树的度:树中节点的最大的度。
层次:根节点的层次为1,其余节点的层次等于该节点的双亲节点加1。
树的高度:树中节点的最大层次。
无序数:如果树中节点的各子树之间的次序是不重要的,可以交换位置。
有序数:如果树中结点的各子树的次序是重要的,不可以交换位置。
森林:0个或多个不相交的树组成。对森林加上一个根,森林即成为树;删去跟,树即成为森林。
树是由若干个节点组成的分支,每个节点都可能组成一棵树
1.根(root):
树的第一个节点,没有直接前驱,如上图的A
2.双亲节点(parent):
某节点的直接前驱称为该节点的双亲节点,或称为父节点,例如上图A 是B的父节点。
3.孩子节点(child):
某节点的直接后继称为该节点的孩子节点,例如上图中的B、C、D均为 A的孩子节点。
4.节点的层次(level):
从树根开始,根为第一层,根的孩子为第二层,树中节点的最大层次 称为树的深度或者高度。比如上图中节点E的层次是3.
5.节点的度(degree):
节点拥有的子树的数量,称为该节点的度,度为0的节点称为叶子节点或者终端节点,度不为0的节点,称为非终端节点或者分支节点,比如上图节点B的度为2。
6.叶子(leaf):
一棵树中度等于0的节点,称为叶子节点,如上图K,L,G,M,I为叶子
4.二叉树的性质
-
二叉树是一种属性结构,他的特性是每个节点最多只有两棵子树,即二叉树中不存在度大于2的节点,并且二叉树的子树有左右之分,其次序不能任意颠倒
-
二叉树的五种形态:
-
二叉树的特点
性质1:二叉树第i层上的节点数目最多为 2^{i-1} (i≥1)。
性质2:深度为k的二叉树至多有2^{k}-1个节点(k>=1)。
性质3:包含n个节点的二叉树的高度至少为log2 (n+1)。
性质4:在任意一颗二叉树中,若终端节点的个数为n0,度为2的节点数为n2,则n0=n2+1。
性质5:满二叉树:一棵深度为k且具备有2^k-1个节点的二叉树,称为满二叉树,在不改变深度的情况下,无法再往这棵树上加节点
性质6:完全二叉树:满足以下两个条件的二叉树
6.1:除去最后一层后为满二叉树
6.2:最后一层的节点必须一次从左往右边排(左边不排满,就不要排右边),完全二叉树的特性是不浪费空间。
练习1:
节点数为699的完全二叉树的叶子节点的数量为多少?
节点数为699的完全二叉树的叶子节点的数量为多少?
n0+n1+n2 = 699 注释:n0,n1,n2分别对应度为1,2,3的节点
因为节点数为计数的完全二叉树不存在,度为1的节点
所以 n0+n2 699===>n2 = n0-1==>2*n0-1 = 699
- 节点数为奇数的完全二叉树不存在度为1的节点所以n1为0,因为完全二叉树加一个节点就是偶数
699 = 2*n2-0+1 == >n2 = 349
n0 = 349+1 = 350
5.二叉树的存储结构
存储结构:即保存了元素又保存了关系(根据二叉树的信息可以把二叉 树还原)
(1)顺序存储结构
由于在顺序存储,数据之间的逻辑关系是用物理位置来表达,而二叉树中每一个节点都有一个对应的标号,因此可以使用标号来作为数组的下标,但除非是完全二叉树,否则会浪费存储空间,如下图所示:
(2)链式存储结构
链式存储思路与链表类似,使用指针来直接将节点的逻辑关系串联起来,比如:
对于链式存储而言,二叉树的节点设计与链表无异,如下:
typedef struct node { datatype data;// 用户数据 struct node *lchild;// 左子树指针 struct node *rchild;// 右子树指针 }node;
- 节点数为奇数的完全二叉树不存在度为1的节点所以n1为0,因为完全二叉树加一个节点就是偶数
6.树的遍历
所谓遍历,就是按某种规律访问每一个节点,对于之前的线性表而言,遍历方法很简单,就是从头跑到尾,因为线性表是一对一的关系,但是树状结构是非线性的,因此从根节点开始遍历所有节点可以有很多不同的算法,常见的有:
1.前序遍历 : 根节点 - 左子树 - 右子树
2.中序遍历 : 左子树 - 根节点 -右子树
3.后序遍历 : 左子树 - 右子树 - 根节点
4.按层遍历 : 从上到下,从左到右依次访问节点
其中需要注意的是,前中后序遍历,都是递归算法。以前序遍历为例, 当访问完根节点,进而要访问左子树时,由于左子树本身一棵二叉树, 因此也需要进行前序遍历,也是先访问左子树的根节点,然后再依次访 问左子树的左子树和左子树的右子树。比如:
前序遍历的序列是:F - [BADCE] - [GIH],其中,F是根节点,而BADCE是 左子树,GIH是右子树。
对左子树的访问,也符合前序遍历的定义,即 B - [A] -[DCE]
以此类推,对上述二叉树而言;
中序遍历的序列是:[ABCDE] - F - [GHI]
后续遍历的序列是: [ACEDB] - [HIG] - F
至于按层遍历,就是按照字面意思理解即可,序列是FBGADICEH
课堂练习2
假设右如下二叉树,请写出其前序遍历,中序遍历,后序遍历以及层次遍历的序列。
前序:ABCDEFGHK
中序:BDCAEHGKF
后序:DCBHKGFEA
至于按层遍历,就是按照字面意思理解即可,序列是FBGADICEH