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