一:
动
态
查
找
树
表
一、二叉排序树
(二叉查找树)
1.定义:
二叉排序树
或者是一棵空树;或者
是具有如下特性的二叉树:
(
1
)若它的左子树不空,则左子树上
所有
结点的值
均小于
根结点的值;
(
2
)若它的右子树不空,则右子树上
所有结点的值均大于根结点的值
;
(
3
)它的左、右子树
也都
分别
是二叉
排序树
。
2.查找算法:
在二叉排序树上进行查找,
是一个从根结点开
始,沿某一个分支逐层向下进行比较判等的过程。
它可以是一个递归的过程。
若二叉排序树
为空
,
则查找不成功;否则
1)若给定值
等于
根结点的关键字,则查找成功;
2)若给定值
小于
根结点的关键字,则继续在左子树上进行查找;
3)若给定值
大于
根结点的关键字,则继续在右子树上进行查找
。
从根结点出发,沿着左分支或右分支逐层向
下直至关键字等于给定值的结点
;
——
查找成功
从根结点出发,沿着左分支或右分支逐层向
下直至指针指向空树为止。
——
查找不成功
3.二叉排序树的插入算法
•
根据动态查找表的定义,“插入”操作在查找不成功时才进行;
•
若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个
新的叶
子结点
,其插入位置由查找过程得到。
•
为了向二叉排序树中插入一个新元素,必须先检查这个元素是否在树中已经存在,即:在插入之前,先使用查找算法在树中检查要插入元素有还是没有。
搜索成功
:
树中已有这个元素,不再插入。
搜索不成功
:
树中原来没有关键字等于给
定值的结点,把新元素加到查找操作停止的地方。
3.
二叉排序树
-
插入算法
。
BstTree BstInsert ( BstTree &t, ElemType x )
{
// 查找 x
p = t; f = NULL; // f 指向p的双亲
while ( p ) {
if ( p->data==x ) return p; //查找成功
else if ( x<p->data ) { f = p; p = p->lchild; }
else { f = p; p = p->rchild; }
}
}
// 未找到,插入 x
3.
二叉排序树
-
插入算法
// 未找到,插入 x
s = malloc(sizeof(BiNode));
s->data = x;
s->lchild = s->rchild = NULL;
if ( f == NULL )
t = s; // 插入s为根结点
else if ( x < f->data ) f->lchild = s;
else f->rchild = s;
return s; // 返回插入结点
}
4.二叉排序树的删除算法
和插入相反,删除在
查找成功
之后进行,并且要
求在删除二叉排序树上某个结点之后,
仍然保持
二叉排序树的特性
。
•
在二叉排序树中删除一个结点时,必须将因删除结点而断开的二叉链表重新链接起来,同时确保二叉排序树的性质不会失去。
•
为保证在执行删除后,树的搜索性能不至于降低,还需要防止重新链接后树的高度增加
可分
三种情况
讨论:
•
(1)被删除的结点
是叶子
;
•
(2)被删除的结点
只有左子树
或者
只有
右子树
;
•
(3)被删除的结点
既有左子树,也有右
子树
。
4.
二叉排序树
-
删除算法
BstTree BstDelete ( BstTree &t, ElemType x ) { // 查找 x p = t; f = NULL; // f 指向p的双亲 while ( p ) { if ( p->data==x ) break; //查找成功,进行删除 else if ( x<p->data ) { f = p; p = p->lchild; } else { f = p; p = p->rchild; } } } // 若找到,删除结点 if ( p != NULL ) …
2. 二叉排序树 - 删除算法
// 若找到,删除结点 if ( p != NULL ) { // a) p 是叶子 if ( p->lchild==NULL && p->rchild==NULL ) { if ( f==NULL ) t = NULL; else { if ( f->lchild==p ) f->lchild = NULL; else f->rchild = NULL; } }// b) p 只有一个子树 else if ( p->lchild==NULL ) { // p只有右子树 if ( f==NULL ) t = p->rchild; else { if ( f->lchild==p ) f->lchild = p->rchild; else f->rchild = p->rchild; } } else if ( p->rchild==NULL ) { // p只有左子树 if ( f==NULL ) t = p->lchild; else { if ( f->lchild==p ) f->lchild = p->lchild; else f->rchild = p->lchild; } }// b) p 既有左子树也有右子树 else { //找到p的“前驱”(左子树上的最大元素) q = p; s = p->lchild; while ( s->rchild ) { q = s; s = s->rchild; } //用p的“前驱”s代替p p->data = s->data; //删除s(s的右子树必然为空) if ( q != p ) q->rchild = s->lchild; else q->lchild = s->lchild; } } // if (p!=NULL) // 返回被删除结点,若未找到时返回NULL return p; }
5.查找性能的分析
对于每一棵特定的二叉排序树,均可按
照平均查找长度的定义来求它的
ASL
值,显然,由值相同的
n
个关键字,构
造所得的不同形态的各棵二叉排序树的
平均查找长
度的值不同,甚至可能差
别很大。
四:
二叉平衡树
AVL
树的定义
一棵AVL树或者是空树,或者是具有下列性质的二叉搜索树:
它的左子树和右子树都是
AVL
树,且左
子树和右子树的高度之差的绝对值不超过
1
。
二叉平衡树的
特点为:
树中每个结点的
左、右子树深度之差的
绝对值不大于
1
结点的平衡因子
balance
(balance factor)
•
每个结点附加一个数字,给出该结点左子树的高度减去右子树的高度所得的高度差。这个数字即为结点的
平衡因子balance
。
根据
AVL
树的定义,任一结点的平
衡因子只能取
-1
,
0
和
1
。
如果一个结点的平衡因子的绝对值大于1,则这棵二叉搜索树就失去了平衡,不再是AVL树。
•
如果一棵二叉搜索树是高度平衡的,它就成为
AVL
树
。如果它有
n
个结点,其高度可保持在O(log
2
n
),平
均搜索长度也可保持在O(log
2
n
)。
平衡化旋转
•
如果在一棵平衡的二叉搜索树中插入一个新结点,造成了不平衡。此时必须调整树的结构,使之平衡化。
•
平衡化旋转有两类:
–
单旋转
(左旋和右旋)
–
双旋转 (左平衡和右平衡)
•
每插入一个新结点时,AVL树中相关结点的平衡状态会发生改变。因此,在插入一个新结点后,需要
从插入
位置沿通向根的路径回溯
,
检查各结点的平衡
因子(左、右子树的高度差)
。
如果在某一结点
发现高度不平衡,停止回溯。
•
从发生不平衡的结点起,沿刚才回溯的路径取直接下两层的结点。
•
如果这三个结点处于一条直线上,则采用单旋转进行
平衡化
。单旋转可按其方向分为左单旋转和右单旋转,其中一个是另一个的镜像,其方向与不平衡的形状相关。
•
如果这三个结点处于一条折线上,则采用双旋转进行
平衡化
。双旋转分为先左后右和先右后左两类。
例,输入关键码序列为
{ 16, 3, 7, 11, 9,
26, 18, 14, 15 }
,
插入和调整过程如下。
平衡树的查找性能分析:
在平衡树上进行查找的过程和二叉排序树相
同,因此,
查找过程中和给定值进行比较的
关键字的个数不超过平衡
树的深度。
问:含
n
个关键字
的二叉平衡树
可能达
到的最大深度
是多少?