中国海洋大学 考研 期末 数据结构 第九章 查找Word下载.docx
《中国海洋大学 考研 期末 数据结构 第九章 查找Word下载.docx》由会员分享,可在线阅读,更多相关《中国海洋大学 考研 期末 数据结构 第九章 查找Word下载.docx(51页珍藏版)》请在冰豆网上搜索。
=(b))
…
//--对字符串型关键字
#defineEQ(a,b)(!
strcmp((a)==(b)))
#defineLT(a,b)(!
strcmp((a)==(b))<
0)
#defineLQ(a,b)(!
=0)
9.1静态查找表
抽象数据定义:
ADTStaticSearchTable{
数据对象D:
D是具有相同特性的数据元素的集合。
每个数据元素含有类型相同的关键字,可唯一标识数据元素。
数据关系R:
数据元素同属一个集合。
基本操作P:
Create(&
ST,n);
Destroy(&
ST);
Search(ST,key);
Traverse(ST,Visit());
}ADTStaticSearchTable
操作结果:
构造一个含n个数据元素的静态查找表ST。
Destroy(&
初始条件:
静态查找表ST存在;
销毁表ST。
Search(ST,key);
静态查找表ST存在,key为和查找表中元素的关键字类型相同的给定值;
若ST中存在其关键字等于key的数据元素,则函数值为该元素的值或在表中的位置,否则为“空”。
Traverse(ST,Visit());
静态查找表ST存在,Visit是对元素操作的应用函数;
按某种次序对ST的每个元素调用函数Visit()一次且仅一次,一旦Visit()失败,则操作失败。
假设静态查找表的顺序存储结构为
typedefstruct{
//数据元素存储空间基址,建表时
//按实际长度分配,0号单元留空
intlength;
//表的长度
}SSTable;
数据元素类型的定义为:
keyTypekey;
//关键字域
……//其它属性域
}ElemType;
9.1.1顺序表的查找
以顺序表或线性链表表示静态查找表
回顾顺序表的查找过程:
假设给定值e=64,
要求ST.elem[k]=e,问:
k=?
intlocation(SqListL,ElemType&
e,
Status(*compare)(ElemType,ElemType)){
k=1;
p=L.elem;
while(k<
=L.length&
&
!
(*compare)(*p++,e)))k++;
if(k<
=L.length)returnk;
elsereturn0;
}//location
intSearch_Seq(SSTableST,
KeyTypekey){
//在顺序表ST中顺序查找其关键字等于
//key的数据元素。
若找到,则函数值为
//该元素在表中的位置,否则为0。
ST.elem[0].key=key;
//“哨兵”
for(i=ST.length;
ST.elem[i].key!
=key;
--i);
//从后往前找
returni;
//找不到时,i为0
}//Search_Seq
算法9.1
分析顺序查找的时间性能
定义:
查找算法的平均查找长度ASL
(AverageSearchLength)
为确定记录在查找表中的位置,需和给定值进行比较的关键字个数的期望值。
其中:
n为表长,Pi为查找表中查找第i个记录的概率,且∑Pi=1,Ci为找到该记录时,和给定值比较过的关键字的个数。
对顺序表而言,Ci=n-i+1
ASL=nP1+(n-1)P2++2Pn-1+Pn
在等概率查找的情况下,
顺序表查找的平均查找长度为:
在不等概率查找的情况下,ASLss在
Pn≥Pn-1≥·
·
≥P2≥P1
时取极小值
若查找概率无法事先测定,则查找过程采取的改进办法是,在每次查找之后,将刚刚查找到的记录直接移至表尾的位置上。
9.1.2有序表的查找
上述顺序查找表的查找算法简单,但平均查找长度较大,特别不适用于表长较大的查找表。
若以有序表表示静态查找表,则查找过程可以基于“折半”进行。
例如:
key=64的查找过程如下:
low指示查找区间的下界
high指示查找区间的上界
mid=(low+high)/2
intSearch_Bin(SSTableST,KeyTypekey){
//在有序表ST中折半查找其关键字等于key的数据元素。
//若找到,则函数值为该元素在表中的位置,否则为0。
low=1;
high=ST.length;
//置区间初值
while(low<
=high){
mid=(low+high)/2;
if(EQ(key,ST.elem[mid].key))
returnmid;
//找到待查元素
elseif(LT(key,ST.elem[mid].key))
high=mid-1;
//继续在前半区间进行查找
elselow=mid+1;
//继续在后半区间进行查找
}
return0;
//顺序表中不存在待查元素
}//Search_Bin
算法9.2
分析折半查找的平均查找长度
一般情况下,表长为n的折半查找的判定树的深度和含有n个结点的完全二叉树的深度相同。
假设n=2h-1并且查找概率相等
则
在n>
50时,可得近似结果
9.1.3静态树表的查找
在不等概率查找的情况下,折半查找不是有序表最好的查找方法。
关键字:
ABCDE
Pi:
0.20.30.050.30.15
Ci:
23123
此时ASL=20.2+30.3+10.05+20.3+30.15=2.4
若改变Ci的值21323
则ASL=20.2+10.3+30.05+20.3+30.15=1.9
定义:
使
达最小的判定树称为最优二叉树,
介绍一种次优二叉树的构造方法:
为计算方便,令wi=pi
选择二叉树的根结点,
使达最小
为便于计算,引入累计权值和
并设wl-1=0和swl-1=0,
则推导可得
所得次优二叉树如下所示:
查找比较“总次数”=32+41+25+33+14+33+25=52
和折半查找相比较
查找比较“总次数”=32+21+35+13+34+23+35=59
构造次优二叉树的算法
StatusSecondOptimal(BiTree&
T,ElemTypeR[],
floatsw[],intlow,inthigh){
//由有序表R[low..high]及其累计权值表sw
//递归构造次优查找树T。
选择最小的ΔPi值
if(!
(T=(BiTree)malloc(sizeof(BiTNode))))
returnERROR;
T->
data=R[i];
//生成结点
if(i==low)T->
lchild=NULL;
//左子树空
elseSecondOptimal(T->
lchild,R,sw,low,i-1);
//构造左子树
if(i==high)T->
rchild=NULL;
//右子树空
elseSecondOptimal(T->
rchild,R,sw,i+1,high);
//构造右子树
returnOK;
}//SecondOptimal
算法9.3
次优查找树采用二叉链表的存储结构
StatusCreateSOSTre(SOSTree&
T,SSTableST){
//由有序表ST构造一棵次优查找树T
//ST的数据元素含有权域weight
if(ST.length=0)T=NULL;
else{
FindSW(sw,ST);
//按照有序表ST中各数据元素
//的weight值求累计权值表
SecondOpiamal(T,ST.elem,sw,1,ST.length);
}//CreatSOSTree
算法9.4
9.1.4索引顺序表的查找
若以索引顺序表表示静态查找表,则Search函数可用分块查找来实现。
分块查找又称索引顺序查找,是顺序查找的一种改进方法。
在此方法中,除表本身外,还需建立一个‘索引表’。
如下图所示:
查找过程:
1)由索引确定记录所在区间;
2)在顺序表的某个区间内进行查找。
可见,索引顺序查找的过程也是一个“缩小区间”的查找过程。
注意:
索引可以根据查找表的特点来构造。
索引顺序查找的平均查找长度=查找“索引”的平均查找长度+查找“顺序表”的平均查找长度
9.2动态查找表
抽象数据类型动态查找表的定义如下:
ADTDynamicSearchTable{
每个数据元素含有类型相同的关键字,可唯一标识数据元素。
基本操作P:
InitDSTable(&
DT)
DestroyDSTable(&
SearchDSTable(DT,key);
InsertDSTable(&
DT,e);
DeleteDSTable(&
T,key);
TraverseDSTable(DT,Visit());
}ADTDynamicSearchTable
DT);
构造一个空的动态查找表DT。
动态查找表DT存在;
销毁动态查找表DT。
动态查找表DT存在,key为和关键字类型相同的给定值;
若DT中存在其关键字等于key的数据元素,则函数值为该元素的值或在表中的位置,否则为“空”。
动态查找表DT存在,e为待插入的数据元素;
若DT中不存在其关键字等于e.key的数据元素,则插入e到DT。
若DT中存在其关键字等于key的数据元素,则删除之。
动态查找表DT存在,Visit是对结点操作的应用函数;
按某种次序对DT的每个结点调用函数Visit()一次且至多一次。
一旦Visit()失败,则操作失败。
综合上一节讨论的几种查找表的特性:
查找插入删除
无序顺序表(n)
(1)(n)
无序线性链表(n)
(1)
(1)
有序顺序表(logn)(n)(n)
有序线性链表(n)
(1)
(1)
静态查找树表(logn)(nlogn)(nlogn)
可得如下结论:
1)从查找性能看,最好情况能达到(logn),此时要求表有序;
2)从插入和删除的性能看,最好情况能达到
(1),此时要求存储结构是链表。
9.2.1二叉排序树和二叉平衡树
一、二叉排序树
1.定义:
二叉排序树或者是一棵空树;
或者是具有如下特性的二叉树:
(1)若它的左子树不空,则左子树上所有结点的值均小于根结点的值;
(2)若它的右子树不空,则右子树上所有结点的值均大于根结点的值;
(3)它的左、右子树也都分别是二叉排序树。
通常,取二叉链表作为二叉排序树的存储结构。
typedefstructBiTNode{//结点结构
structBiTNode*lchild,*rchild;
//左右孩子指针
}BiTNode,*BiTree;
2.二叉排序树的查找算法:
若二叉排序树为空,则查找不成功;
否则,
•1)若给定值等于根结点的关键字,则查找成功;
•2)若给定值小于根结点的关键字,则继续在左子树上进行查找;
•3)若给定值大于根结点的关键字,则继续在右子树上进行查找。
从上述查找过程可见,
在查找过程中,生成了一条查找路径:
从根结点出发,沿着左分支或右分支逐层向下直至关键字等于给定值的结点;
——查找成功
或者
从根结点出发,沿着左分支或右分支逐层向下直至指针指向空树为止。
——查找不成功
算法描述如下:
StatusSearchBST(BiTreeT,KeyTypekey,
BiTreef,BiTree&
p){
//在根指针T所指二叉排序树中递归地查找其
//关键字等于key的数据元素,若查找成功,
//则返回指针p指向该数据元素的结点,并返回
//函数值为TRUE;
否则表明查找不成功,返回
//指针p指向查找路径上访问的最后一个结点,
//并返回函数值为FALSE,指针f指向当前访问
//的结点的双亲,其初始调用值为NULL
}//SearchBST
算法9.5
if(!
T)
{p=f;
returnFALSE;
}//查找不成功
elseif(EQ(key,T->
data.key))
{p=T;
returnTRUE;
}//查找成功
elseif(LT(key,T->
SearchBST(T->
lchild,key,T,p);
//在左子树中继续查找
elseSearchBST(T->
rchild,key,T,p);
//在右子树中继续查找
设key=22
3.二叉排序树的插入算法
•根据动态查找表的定义,“插入”操作在查找不成功时才进行;
•若二叉排序树为空树,则新插入的结点为新的根结点;
否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。
StatusInsertBST(BiTree&
T,ElemTypee)
{
//当二叉排序树中不存在关键字等于e.key的
//数据元素时,插入元素值为e的结点,并返
//回TRUE;
否则,不进行插入并返回FALSE
SearchBST(T,e.key,NULL,p))
{}
elsereturnFALSE;
}//InsertBST
算法9.6
s=(BiTree)malloc(sizeof(BiTNode));
//为新结点分配空间
s->
data=e;
lchild=s->
if(!
p)T=s;
//插入s为新的根结点
elseif(LT(e.key,p->
data.key))
p->
lchild=s;
//插入*s为*p的左孩子
elsep->
rchild=s;
//插入*s为*p的右孩子
returnTRUE;
//插入成功
4.二叉排序树的删除算法
和插入相反,删除在查找成功之后进行,并且要求在删除二叉排序树上某个结点之后,仍然保持二叉排序树的特性。
可分三种情况讨论:
(1)被删除的结点是叶子;
(2)被删除的结点只有左子树或者只有右子树;
(3)被删除的结点既有左子树,也有右子树。
(1)被删除的结点是叶子结点
其双亲结点中相应指针域的值改为“空”
(2)被删除的结点只有左子树
或者只有右子树
其双亲结点的相应指针域的值改为“指向被删除结点的左子树或右子树”。
(3)被删除的结点既有左子树,也有右子树
以其前驱替代之,然后再删除该前驱结点
StatusDeleteBST(BiTree&
T,KeyTypekey){
//若二叉排序树T中存在其关键字等于key的
//数据元素,则删除该数据元素结点,并返回
//函数值TRUE,否则返回函数值FALSE
T)returnFALSE;
//不存在关键字等于key的数据元素
else{}
}//DeleteBST
算法9.7
if(EQ(key,T->
{Delete(T);
//找到关键字等于key的数据元素
elseif(LT(key,T->
DeleteBST(T->
lchild,key);
//继续在左子树中进行查找
elseDeleteBST(T->
rchild,key);
//继续在右子树中进行查找
其中删除操作过程如下所描述:
voidDelete(BiTree&
p){
//从二叉排序树中删除结点p,
//并重接它的左子树或右子树
p->
rchild){}
elseif(!
lchild){}
}//Delete
算法9.8
//右子树为空树则只需重接它的左子树
q=p;
p=p->
lchild;
free(q);
//左子树为空树只需重接它的右子树
rchild;
//左右子树均不空
s=p->
while(!
rchild){q=s;
s=s->
//s指向被删结点的前驱
data=s->
data;
if(q!
=p)q->
rchild=s->
elseq->
//重接*q的左子树
free(s);
5.二叉排序树的查找分析
对于每一棵特定的二叉排序树,均可按照平均查找长度的定义来求它的ASL值,显然,由值相同的n个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的值不同,甚至可能差别很大。
不失一般性,假设长度为n的序列中有k个关键字小于第一个关键字,则必有n-k-1个关键字大于第一个关键字,由它构造的二叉排序树:
平均查找长度是n和k的函数
ASL=P(n,k)(0kn-1)。
假设n个关键字可能出现的n!
种排列的可能性相同,则含n个关键字的二叉排序树的平均查找长度:
由此
可类似于解差分方程,此递归方程有解:
二、平衡二叉树
平衡二叉树又称AVL树,是二叉排序树的另一种形式。
它或者是一棵空树,或者是具有下列性质的二叉树:
它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。
结点的平衡因子:
该结点的左子树高度减去它的右子树高度。
即:
平衡二叉树的特点为:
树中每个结点的左、右子树深度之差的绝对值不大于1。
即。
构造二叉平衡(查找)树的方法是:
在插入过程中,采用平衡旋转技术。
一般情况下,假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针为a(即a是离新插入结点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行调整的规律可归纳为下列四种情况:
LL型、LR型、RR型、LR型。
LL型:
即新插入的结点在a的左子树的左子树上。
此时应选择a的左子树的根结点b,然后进行调整;
LR型:
即新插入的结点在a的左子树的右子树上。
此时应选择a的左子树的根结点b,和b的右子树的根结点c,然后进行调整;
RR型:
即新插入的结点在a的右子树的右子树上。
此时应选择a的右子树的根结点b,然后进行调整;
RL型:
即新插入的结点在a的右子树的左子树上。
此时应选择a的右子树的根结点b,和b的左子树的根结点c,然后进行调整;
二叉排序树的类型定义为:
typedefstructBSTNode{
ElemTypedate;
//结点的平衡因子
intbf;
//左