9.2动态查找树表
抽象数据类型动态查找表的定义如下:
ADT DynamicSearchTable
{
数据对象D:D是具有相同特性的数据元素的集合。每个数据元素含有类型相同的关键字,可唯一标识数据元素。
数据关系R:数据元素同属一个集合
}
基本操作P:
InitDSTable(&DT);
DestroyDSTable(&DT);
SearchDSTable(DT, key);
InsertDSTable(&DT, e);
DeleteDSTable(&T, key);
TraverseDSTable(DT, Visit());
InitDSTable(&Dt);
操作结果:
构造一个空的动态查找表DT。
DestryDSTable(&DT);
初始条件:动态查找表DT存在
操作结果:销毁动态查找表DT
SearchDSTable(DT, key);
初始条件:动态查找表DT存在,key为关键字类型相同的给定值;
操作结果:若DT中存在关键字等于key的数据元素,则函数值为元素的值或在表中的位置,否则为“空”。
InsertDSTable(&DT, e);
初始条件:动态查找表DT存在,e为待插入的数据元素。
操作结果:若DT中不存在其关键字等于e.key的数据元素,则插入e到DT。
DeleteDSTable(&T, key);
初始条件:动态查找表DT存在,key为关键字类型相同的给定值;
操作结果:若DT中存在其关键字等于key的数据元素,则删除之。
9.2动态查找树表
综合上一节讨论的几种炒作表的特性
| 查找 | 插入 | 删除 |
无序顺序表 | O(n) | O(1) | O(n) |
无序线性链表 | O(n) | O(1) | O(1) |
有序顺序表 | O(logn) | O(n) | O(n) |
有序线性链表 | O(n) | O(1) | O(1) |
静态查找树表 | O(logn) | O(nlogn) | O(nlogn) |
儿科得如下结论:
1) 从查找性能看,最好情况能达到O(logn),此时要求表有序
2) 从插入删除的性能看,最好情况能达到O(1),此时要求存储结构是链表。
一、二叉排序树(二叉查找树)
二、二叉平衡树
三、B-树
四、B+树
五、键树
一、二叉排序树
1. 定义:二叉排序树或者是一棵空树;或者具有如下特征的二叉树:
(1) 若它的左子树不空,则左子树上所有结点的值均小于根结点的值;
(2) 若它的右子树不空,则右子树上所有结点的值均大于根结点的值;
(3) 他的左、右子树也都分别是二叉排序树。
通常,取二叉链表作为二叉排序树的存储结构。
2. 二叉排序树的查找算法
额若二叉排序树为空,则查找不成功;否则
1) 若给定值等于根结点的关键字,则查找成功;
2) 若给定值小于根结点的关键字,则继续在左子树上进行查找;
3) 若给定值大于根结点的关键字,则继续在右子树上进行查找。
算法描述如下:
36_001 |
Status SearchBST(BiTree, KeyType key, BiTree f, BiTree &p) { if(! T) { p = f; return FALSE; //查找不成功 } else if( EQ(key, T->data.key) ) { p = T; return TRUE; //查找成功 } else if( LT(key, T->data.key) ) { SearchBST(T->lchild, key, T, p); //在左子树中继续查找 } else SearchBST(T->rchild, key, T, p); //在右子树中继续查找 }//SearchBST |
3二叉排序树的插入算法
36_002 |
Status InsertBST(BiTree &T, ElemType e) { if(! SearchBST(T, e.key, NULL, p)) { s = (BiTree) malloc(sizeof(BiTNode)); s->data = e; s->lchild = s->rchild = NULL; if(! p) T = s; //插入s为新的根结点 else if( LT(e.key, p->data.key)) p->lchild = s; //插入s为左孩子 else p->rchild = s; //插入s为右孩子 return TRUE; } else return FALSE; }//InsertBST |
4.二叉排序树的删除算法
和插入相反,删除在查找成功之后进行,别且要求在删除二叉排序树的某个结点之后,仍然保持二叉排序树的特性。
可分三种情况讨论:
(1) 被删除的结点是叶子结点
(2) 被删除的结点只有左子树或这只有右子树
(3) 被删除的结点既有左子树,也有右子树
36_003 |
Status DeleteBST(BiTree &T, KeyType key) { if(! p) return FALSE; //不存在关键字等于key的数据元素 else { if(EQ(key, T->data.key)) Delete(T); //找到关键字等于key的数据元素 else if(LT(key, T->data.key) DeleteBST(T->lchild, key); else DeleteBST(T->rchild, key); return TRUE; } }//DeleteBST
//其中删除操作过程如下所描述 void Delete(BiTree &p) { //若二叉排序树T中存在关键字等于key //数据元素时,则删除该数据元素结点p //并返回TRUE;否则返回FALSE if(! p->rchild) { //右子树空则只需重接它的左子树 q = p; p = p->lchild; free(q); } else if(! p->lchild) { //只需重接它的右子树 q = p; p = p->rchild; free(q); } else { //左右子树均不空 q = p; s = p->lchild; while(! s->rchild) { q = s; s = s->rchild; } p->data = s->data; //s指向被删结点前驱 if(q != p) q->rchild = s->lchild; else q->lchild = s->lchild; //重接*q的左子树 free(s) } }//Delete |
5.查找性能的分析
对于每一棵特定的二叉排序树,均可以按照平均查找长度的定义来求它的ASL值,显然,由值相同的n个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的值不同,甚至可能差别很大。
例如:
由关键字序列1,2,3,4,5构造而得的二叉排序树,ASL=(1+2+3+4+5)/5=3
由关键字序列3,1,2,5,4构造而得的二叉排序树,ASL=(1+2+3+2+3)/5=2.2
下面讨论平均情况。
不失一般性,假设长度为n的序列中有k个关键字小于第一个关键字,则必有n-k-1个关键字大于第一个关键字,有它构造的二叉树排序
的平均查找长度是n和k的函数
P(n,k) (0<=k<=n-1)
假设n个关键字可能出现的n!中排列的可能性相同,则含n个关键字的二叉排序树的平均查找长度
在等概率查找的情况下
可类似于解差分方程,此递归方程有解:
二、二叉平衡树
这是另一种形式的二叉查找树,其特点为:左、右子树深度之差的绝对值不大于1。称有这种特性的二叉树为平衡树。
构造二叉平衡(查找)树的方法是:在插入过程中,采用平衡旋转技术
平衡树的查找性能分析:
在平衡树上进行查找的过程和二叉排序树相同,因此,查找过程中和给定值进行比较的关键字的个数树不超过平衡树的深度,假设深度为h的二叉平衡树上所有结点数的最小值为Nh,则显然Nh=Nh-1+Nh-2+1
由此可以推到出
因此,在平衡树上进行查找的时间复杂度为O(log(n))