一、树
1.什么是树?
在计算机科学中,树是一种抽象数据类型或是实现这种数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它由有限个节点组成一个具有层次关系的集合。
树之所以叫树,是因为他看起来和我们现实中的树差不多,只是这棵树的根在上面,而树枝和树叶是朝下的,就像一颗倒立的树。
2.树的特点
1.每个节点都只有有限个子节点或无子节点。
2.没有父节点的节点称为根节点。
3.每一个非根节点有且只有一个父节点。
4.除了根节点外。每个子节点可以分为多个不相交的子树。
5.树里面没有环路。
3.关于树的术语
1.节点的度:一个节点含有的子树的个数称为该节点的度;
2.树的度:一棵树中,最大的节点的度称为树的度;
3.叶节点或终端节点:度为零的节点;
4.非终端节点或分支节点:度不为零的节点;
5.父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
6.孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
7.兄弟节点:具有相同父节点的节点互称为兄弟节点;
8.节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
9.深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;
10.高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0; 11.堂兄弟节点:父节点在同一层的节点互为堂兄弟;
12.节点的祖先:从根到该节点所经分支上的所有节点;
13.子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
14.森林:由m(m>=0)棵互不相交的树的集合称为森林;
二、二叉树
1.什么是二叉树?
首先,二叉树是一种特殊的树,在树中每个都只有有限个节点或者没有节点,在二叉树中,每个几点最多只能有两个节点,通常称为 左子树 和 右子树 。
二叉树的左右子树具有左右顺序。不能随意颠倒。
二叉树是一个连通的无环图,并且每一个顶点的度不大于3.
有根二叉树还要满足根节点的度不大于2。
二叉树是一个有根树,并且每个节点最多有2个子节点。
2.二叉树的遍历
1.先序遍历
按照 根节点->左子树->右子树的顺序访问二叉树的遍历方式,称之为二叉树的先序遍历
2.中序遍历
按照 左子树->根节点->右子树的顺序访问二叉树的遍历方式,称之为二叉树的中序遍历。
3.后续遍历
按照 左子树->右子树->根节点的顺序访问二叉树的遍历方式,称之为二叉树的后续遍历。
三、二叉查找树
1.什么是二叉查找树?
二叉查找树,也称为二叉搜索树、有序二叉树、排序二叉树。一棵空树是二叉查找树。具有下列性质的二叉树也是二叉查找树:
1.若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2.若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3.任意节点的左、右子树也分别为二叉查找树;
4.没有键值相等的节点。
2.二叉搜索树的查找算法
在二叉搜索树tree中查找一个节点x的过程如下:
1.如果tree是空树,则搜索失败。
2.如果非空,若x等于tree的根节点的数据域的值,则查找成功。
3.若x小于b的根节点的数据域的值,则继续搜索左子树。
4.若x大于b的根节点的数据域的值,则继续搜索右子树。
3.二叉搜索树的插入算法
在二叉搜索树tree中插入一个节点x的过程如下:
1.若tree是空树,则将s所指节点作为根节点插入。
2.如果非空,若x的数据域的值等于tree的根节点的数据域的值,则返回。
3.如果x的数据域的值小于tree的根节点的数据域的值,则把x节点所指的节点插入左子树。
4.如果x的数据域的值大于tree的根节点的数据域的值,则把x节点所指的节点插入左子树。
4.二叉查找树的删除算法
1.如果要删除的节点是叶子节点,即左右子树都为空,则直接删除。
2.如果要删除的节点只有左子树或者右子树,直接让左子树或者右子树直接成为其父情节点的左子树或者右子树即可。
3.若左右子树都不为空,则删去之后要进行调整,使其保持二叉搜索树的特性。
四、红黑树
1.什么是红黑树?
红黑树是一种特殊的二叉查找树。特殊就特殊在红黑树的每个节点上都有存储着该节点的颜色,可以是红色或者黑色。
2.红黑树的特性:
1.红黑树的每个节点不是黑色就是红色,必须有色。
2.红黑树的根节点必须是黑色的。
3.红黑树的每个叶子节点(NIL)是黑色的[这里的叶子结点,是指为空的叶子结点]。
4.如果一个节点是红色的,则它的子节点必须是黑色的。
5.从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑色节点。
四、红黑树的再平衡
添加或者删除红黑树节点之后,红黑树就发生了变化,可能不会再满足红黑树的5条性质,也就不再是一棵红黑树了,要使其在成为红黑树,就需要进行树的再平衡。平衡的方式有 左旋 和 右旋 两种方式
1.左旋
对一个节点进行左旋,意味着将该节点变为一个左节点。
a.左线的过程:
1.将Y的左孩子设为X的右孩子,即将B设为X的右孩子。
2.将X设为Y的左孩子的父亲,即将B的父亲设为X。
3.将X的父亲设为Y的父亲。
4.下面分情况讨论:
情况1:如果X的父亲是空节点,则将Y直接设置为根节点。
情况2:如果X是它父节点的左孩子,则将Y设为X的父节点的左孩子。
情况3:如果X是它父节点的右孩子,则将Y设为X的父节点的右孩子。
5.将X设为Y的左孩子。
6.将X的父节点设为Y。
b.左旋的伪代码
LEFT-ROTATE(T, x) y ← right[x] // 前提:这里假设x的右孩子为y。下面开始正式操作 right[x] ← left[y] // 将 “y的左孩子” 设为 “x的右孩子”,即 将β设为x的右孩子 p[left[y]] ← x // 将 “x” 设为 “y的左孩子的父亲”,即 将β的父亲设为x p[y] ← p[x] // 将 “x的父亲” 设为 “y的父亲” if p[x] = nil[T] then root[T] ← y // 情况1:如果 “x的父亲” 是空节点,则将y设为根节点 else if x = left[p[x]] then left[p[x]] ← y // 情况2:如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子” else right[p[x]] ← y // 情况3:(x是它父节点的右孩子) 将y设为“x的父节点的右孩子” left[y] ← x // 将 “x” 设为 “y的左孩子” p[x] ← y // 将 “x的父节点” 设为 “y”
2.右旋
对一个节点进行右旋,意味着将该节点变为一个右节点。
a.左线的过程:
1.将X的右孩子设为Y的左孩子,即将B设为Y的左孩子。
2.将Y设为X的右孩子的父亲,即将B的父亲设为Y。
3.将Y的父亲设为X的父亲。
4.下面分情况讨论:
情况1:如果Y的父亲是空节点,则将X直接设置为根节点。
情况2:如果Y是它父节点的右孩子,则将X设为Y的父节点的右孩子。
情况3:如果Y是它父节点的左孩子,则将X设为Y的父节点的左孩子。
5.将Y设为X的左右孩子。
6.将Y的父节点设为X。
b.左旋的伪代码
RIGHT-ROTATE(T, y) x ← left[y] // 前提:这里假设y的左孩子为x。下面开始正式操作 left[y] ← right[x] // 将 “x的右孩子” 设为 “y的左孩子”,即 将β设为y的左孩子 p[right[x]] ← y // 将 “y” 设为 “x的右孩子的父亲”,即 将β的父亲设为y p[x] ← p[y] // 将 “y的父亲” 设为 “x的父亲” if p[y] = nil[T] then root[T] ← x // 情况1:如果 “y的父亲” 是空节点,则将x设为根节点 else if y = right[p[y]] then right[p[y]] ← x // 情况2:如果 y是它父节点的右孩子,则将x设为“y的父节点的左孩子” else left[p[y]] ← x // 情况3:(y是它父节点的左孩子) 将x设为“y的父节点的左孩子” right[x] ← y // 将 “y” 设为 “x的右孩子” p[y] ← x // 将 “y的父节点” 设为 “x”
3.区分左旋和右旋
左旋和右旋是对称的。无论左旋还是右旋,在旋转前后都是一颗二叉查找树。
左旋示意图(以X为节点进行左旋) |-----------------------------------------------------------------| | X Z | | / \ ---(左旋)---> / | | Y Z X | | / | | Y | |-----------------------------------------------------------------|
对x进行左旋,意味着,将“X的右孩子”设为“X的父亲节点”;即,将 X变成了一个左节点(X成了为Z的左孩子)!。 因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。
右旋示意图(以节点X进行右旋) |----------------------------------------------------------------| | X Y | | / \ -----(右旋)----> \ | | Y Z X | | \ | | Z | |----------------------------------------------------------------|
对X进行右旋,意味着,将“X的左孩子”设为“X的父亲节点”;即,将 X变成了一个右节点(X成了为Y的右孩子)! 因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。