树代表一种非线性的数据结构,如果一组节点之间存在复杂的一对多关联时,程序就可以考虑使用树来保存这组数据。
树的两种表示法:
父节点表示法:每个节点都记录它的父节点。
树中除了根节点之外的每个节点都有一个父节点,为了记录树中节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父节点。
特点:每个节点可以快速找到他的父节点,但是查找子节点比较麻烦,需要遍历整棵树。
孩子链表示法:每个非叶子节点通过一个链表来记录它所有的子节点。
为每个节点维护一个子节点链,通过该子节点链来记录该节点的所有子节点。
特点:每个节点可以快速找到它的所以子节点,但查找父节点比较麻烦,需要变量整棵树。
二叉树:
二叉树:指的是每个节点最多只能有两个子树的有序树,通常左边的子树称为“左子树”,右边的子树称为“右子树”,由此可得二叉树依然是树,它是一种特殊的树。
完全二叉树:如果一棵二叉树除了最后一层外,就其余的所有节点都是满的,并且最后一层或者是满的,或者仅在右边缺少若干连续的节点,则此树为完全二叉树。
满二叉树:当完全二叉树最后一层的所有节点都是满的,这棵完全二叉树就变成了满二叉树。深度为k的满二叉树包含节点数为2的k次方-1。
二叉树性质:
二叉树第i层上节点数目至多2的i-1次方;
深度为k的二叉树至多有2的k次方减1个节点(满二叉树);
在任何二叉树中如果叶子节点数量为n0,度为2的子节点数量为n2,则n0=n2+1;
具有n个节点的完全二叉树深度为log2 n + 1 ;
对于有n个节点的完全二叉树的节点编号,对任意编号为i的节点具有以下性质:
i==1,节点为根;若i>1,则父节点为i/2 ;
若2i<n,则i有左孩子,编号为2i,否则无左孩子,并且是叶子节点;
若2i+1<n,则i有右孩子,编号为2i+1,否则无右孩子;
1~n/2都是具有孩子节点的非叶子节点,其余的都是叶子节点;编号n/2的节点可能只有左孩子,也可能具有左右孩子
二叉树的顺序存储:
顺序存储指的是充分利用满二叉树的特性,深度为i的二叉树最多包含2的i次方-1个节点,所以定义此长度数组即可以存储这二叉树。
对于普通二叉树(非满二叉树),空出来的节点对应数组元素留空,因此顺序存储会造成空间浪费(如果是完全二叉树就不会浪费空间,如果该二叉树全部是右子节点,浪费空间相当大)。
特点:对于顺序存储的二叉树,不管遍历树中节点,还是查找树中节点,都可以非常高效,唯一缺点是空间浪费大。
二叉树的二叉链表存储:
二叉链表思想是让每个节点记住它的左右子节点。
程序用链表来记录树中的所以节点,所以添加节点没有限制,而且不会像顺序存储那样浪费大量的空间,但是这种存储方式在遍历的时候效率不高,访问指定父节点也比较麻烦,必须用遍历二叉树来搜索父节点。
二叉树的三叉链表存储:
三叉链表思想是让每个节点记住它的左右节点和父节点。
增加了一个节点的存储空间,可以方便的访问父节点。
遍历二叉树:
遍历二叉树指的是按某种规律依次访问二叉树的每个节点,对二叉树的遍历过程就是将非线性结构的二叉树中的节点排列在一个线性序列的过程。
如果采用顺序结构保存二叉树,遍历很容易,直接遍历底层数组即可;
如果采用链式保存二叉树,可有两种遍历方式:深度遍历方式(先访问到树节点最底层次的节点)和广度遍历方式(将逐层访问每层的节点,也称为按层遍历)。
对于深度遍历可分为:先序遍历、中序遍历、后序遍历。
红黑树:
排序二叉树在只有左子节点或者只有右子节点时,会变成普通链表,效率会很差。为了改变排序二叉树存在的不足,Rudolf Bayer发明了另一种改进后的排序二叉树---红黑树。
红黑树在排序二叉树上增加的要求:
(1)每个节点要么是红色,要么是黑色;
(2)根节点永远是黑色;
(3)所有叶子节点都是空节点,并且都是黑色;
(4)每个红色节点的两个子节点都是黑色(不会有两个相连的红色节点);
(5)从任一节点到其子树中每个叶子节点的路径都包括相同数量的黑色节点(根节点到叶子节点包含的黑色节点数称为树的“黑色高度02”)。