树型结构,不同于线性数据结构,比较适合用于描述分支结构的数据,这种数据之间可能有祖先和后代的关系。在计算机中树型结构应用非常广泛,例如在文件系统和数据库系统中,树是组织信息的重要形式之一。
树的定义
树是一种数据结构,是由 n(n ≥ 1)个有限节点组成的一个具有层次关系的集合。
树的示意图如下:
之所以被叫做树,是因为看起来像一棵倒过来的树,也就是根朝上,叶朝下。从上图中可以看出它具有如下特点:
-
每个节点有零个或者多个子节点,树是一个递归的定义,即树的定义中又用到了树的概念。
-
没有父节点的节点称为根节点。
-
每一个非根节点有且只有一个父节点。
-
除了根节点外,每个子节点可以分为多个不相交的子树。
树的相关术语
节点:包含数据项及指向其他结点的分支。
节点的度:结点所拥有的子树的棵树。
叶节点:度为 0 的结点,也被称为终端结点。
分支节点:除了叶结点之外的其他结点,又称为非终端结点。
子女结点:若结点 x 又子树,则子树的根节点即为结点 x 的子女。
父节点:若结点 x 又子女,它就是子女结点的父节点。
兄弟节点:同一父节点的子女称为兄弟。
祖先节点:从根节点到该节点所经分支上的所有结点。
子孙节点:某一结点的子女,以及这些子女的子女都是该结点的子孙。
结点层次:从根到该结点所经路径上的分支条数。
树的深度:树中距离根节点最远的结点所处层次即为树的深度。
树的高度:可以理解为树的深度,高度于深度数值相等。叶结点的高度为 1,非叶结点的高度等于它的子女结点的高度的最大值加 1。
树的度:树中结点的度的最大值。
有序树:如果树中结点的各子树之间的次序是重要的, 不可以交换位置。
无序树:如果树中结点的各子树之间的次序是不重要的,可以交换位置。
森林:是 m 棵树的集合。删去一颗非空树的根节点,树就变成了森林;反之增加一个根节点,让森林的每一棵树的根节点都变成它的子女,森林就变成了一棵树。
二叉树
二叉树的概念
二叉树是每个结点最多只有两个子树的树型结构。它有五种基本形态:
二叉树的性质
二叉树有以下几个性质:
性质1:二叉树第 i 层上的结点数目最多为 2i-1 (i≥1)。
性质2:深度为 k 的二叉树至多有 2k - 1 个结点 (k≥1)。
性质3:包含 n 个结点的二叉树的高度至少为 log2 (n+1)。
性质4:在任意一棵二叉树中,若叶子结点的个数为 n0,度为 2 的结点数为 n2,则 n0 = n2+1。
性质1:二叉树第 i 层上的结点数目最多为 2i-1 (i≥1)。
证明:下面用“数学归纳法”进行证明。
(1) 当 i=1 时,第 i 层的节点数目为 2i-1 = 20 = 1。因为第 1 层上只有一个根结点,所以命题成立。
(2) 假设当 i>1 时,第 i 层的节点数目为 2i-1。这个是根据 (1) 推断出来的。
下面根据这个假设,推断出“第 (i+1) 层的节点数目为 2i”即可。
由于二叉树的每个结点至多有两个孩子,故“第 (i+1) 层上的结点数目”最多是“第 i 层的结点数目的 2 倍”。即第 (i+1) 层上的结点数目最大值 = 2×2i-1 = 2i。
故假设成立,原命题得证。
性质2:深度为 k 的二叉树至多有 2k-1 个结点 (k≥1)。
证明:在具有相同深度的二叉树中,当每一层都含有最大结点数时,其树中结点数最多。利用“性质 1”可知,深度为 k 的二叉树的结点数至多为:
20 + 21 + … + 2k-1 = 2k - 1。
故原命题得证!
性质3:包含 n 个结点的二叉树的高度至少为 log2 (n+1)。
证明:根据“性质 2”可知,高度为 h 的二叉树最多有 2h–1 个结点。反之,对于包含 n 个节点的二叉树的高度至少为 log2(n+1)。
性质4:在任意一棵二叉树中,若叶子结点的个数为 n0,度为 2 的结点数为 n2,则 n0 = n2+1。
证明:因为二叉树中所有结点的度数均不大于 2,所以:结点总数(记为n) = 0度结点数(n0) + 1度结点数(n1) + 2度结点数(n2) 。由此,得到等式一:
(等式一):n = n0 + n1 + n2
另一方面,0 度结点没有孩子,1 度结点有一个孩子,2 度结点有两个孩子,故二叉树中孩子结点总数是:n1 + 2n2。此外,只有根不是任何结点的孩子。故二叉树中的结点总数又可表示为等式二。
(等式二) n = n1 + 2n2 + 1
由等式一和等式二计算得到:n0 = n2 + 1。原命题得证!
满二叉树和完全二叉树
满二叉树
定义:如果二叉树的高度为 h,且有 2h-1 个结点的二叉树,被称为满二叉树。
满二叉树的示意图如下:
完全二叉树
定义:一颗二叉树中,只有最下面两层结点的度可以小于 2,并且最下一层的叶结点集中在靠左的若干位置上。这样的二叉树称为完全二叉树。
特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。显然,一棵满二叉树必定是一棵完全二叉树,而完全二叉树未必是满二叉树。
完全二叉树的示意图如下:
二叉树的存储
数组存储表示
在数据处理过程中,二叉树的大小和形态不发生剧烈变化的场合,可以采用数组方式来表示二叉树的抽象数据类型,采用数组的下标为索引,随机存取二叉树的结点,这种方法兼顾树形结构特点,使各结点能够方便定位它的父节点和左、右子女,最经典的案例就是堆排序。
这种方法,比较适用于完全二叉树的存储,通过数组下标,可以计算出当前节点的父节点以及子节点对应的数组下标。但是如果将一般的二叉树存放在一维数组中,为了模仿完全二叉树的效果,如果一个结点只有一个左孩子,那么仍然需要将右孩子的位置空出来,这样才能反映二叉树节点的关系,这样做可能会消耗内存,比如单支树的情况。
对于下面的完全二叉树进行数组存储表示:
使用数组存储时:
有一个 n 个结点的完全二叉树自顶向下,同一层自左向右连续给结点编号 1, 2, 3, …, n, 然后按此结点编号将树中各结点顺序的放在一个一维数组中,并简称编号为 i 的结点为结点 i,则有以下关系:
-
若 i == 1,则结点 i 为根,无父节点;若 i > 1,则结点 i 的父节点为结点 i/2。
-
若 2 * i <= n,则结点 i 的左子女为结点 2 * i。
-
若 2 * i + 1 <= n,则结点 i 的右子女为结点 2 * i + 1。
-
若结点编号 i 为奇数,且 i != 1,它处于右兄弟位置,则它的左兄弟为结点 i - 1。
-
若结点编号 i 为偶数,且 i != n,它处于左兄弟位置,则它的右兄弟为结点 i + 1。
-
结点 i 所在的层次为 log2i + 1。
链表存储表示
数组存储的方式对于完全二叉树的存储表示非常有效,但是表示一般二叉树,尤其是形态剧烈变化的二叉树,存储空间的利用不是很理想,因此使用链表的表示方法,完全可以客服这些缺点。
根据二叉树的定义,二叉树的每一个结点可以有两个分支,分别指向结点的左、右子树。因此二叉树的结点至少应当包括三个域分别是存放节点的数据 data、左孩子结点指针 leftChild、右孩子结点指针 rightChild。整个二叉树链表有一个表头指针,它指向二叉树的根节点,作用是当作树的访问点。
二叉树的链表表示如下:
对于下面的二叉树使用链表存储如下:
以上就是树以及二叉树的基本概念,其具体的实现,将会在后面的文章中详细介绍。