查找
一、查找的基本概念
查找表;可以看作一个新的ADT工具类,数据对象是候选记录的集合,核心操作是
查找
静态查找表:一旦创建,主要是查找,很少或者不进行记录的插入和删除
动态查找表:查找的同时会对查找表进行插入、删除
关键字:记录中某个数据项的值,可用来识别一个记录
主关键字:唯一标识数据元素
次关键字;可以标识若干个数据元素
二、静态查找表的查找
定义:
typedef *** KeyType;//关键字类型
typedef struct Search{
KeyType key;
...;
}ElemType;//记录类型
typedef struct {
ElemType *elem;//数组首地址
int length;//表长,有效记录
}SSTable;
静态查找表上的顺序查找:
//添加哨兵,从后向前比较,省略每次循环事的越界检查
int Seach_Seq(SSTable ST,KeyType key)
{
ST.elem[0].key=x;
for(i=ST.length;St.elem[i].key!=x;--i)
return i;
}
*记第i个元素被查找的概率为Pi, 顺序查找平均查找长度:ASLSS=n*P1+(n-1)*P2+..+Pi*(n-i+1)+..+1*Pn +(n+1)*P0
*通常所查找元素均在ST中,此时P0=0
*若查找保证成功且各元素几率同(1/n)则ASLSS=(n+1)/2
*若成功与否各50%,且各元素被找到概率同(1/2n)则
ASLSS=(n+1)/4 + (n+1)/2= 3*(n+1)/4
*概率越大越靠后放则ASL越小,事先不知概率可动态调整
折半查找
int Search_Bin(SSTable ST,
KeyType key){
low=1; high=ST.length;
while (low <= high) {
mid = (low + high) / 2;
if (key=ST.elem[mid].key)
return mid;
else if(key<ST.elem[mid].key)
high=mid-1;
else
low = mid + 1;
}
return 0;
}
折半查找树
*折半查找的判定树:比较时生成根,到左侧区间比较时生成左孩子,右侧比较生成右
孩子,如此递归。
*折半查找判定树任意节点左右子树深度相差最多为1,可证此类树深度为[log2n]+1,
籍此可证查找成功时最坏查找长度为[log2n]+1,借助方形外部结点可证不成功时的
最大查找长度也如此。
*折半查找判定树特点:右子树中各结点值比根大,左子树中各结点值比根小,子树
也如此.根据判定树可如下完成查找:从根结点开始,比当前元素大就向右、小则
向左,或者找到,或者到空.
*最坏查找长度为[log2n]+1,复杂度O(log2n)
*当判定树为满二叉树时(n=2h-1),若查找成功且各元素几率同(1/n)则ASLbs=(1*20+2*21+3*22+…+h*2h-1)/n≈log2(n+1)-1
对具体例子要会求查找成功和不成功的平均查找长度
分块查找
定义:
对数据进行分块,使得块内无序、块间有序。将各块中的最大关键字构成一个索
引表,表中还要包含每块的起始地址。
过程:
① 对索引表用折半查找定位最后一个小于等于key的块
② 确定了待查关键字所在的块后,块内采用顺序查找
优缺点:
优点:删除或者插入相对容易,移动记录量变小。
缺点:要增加一个索引表的存储空间并对初始索引表进行排序运算。
适用情况:如果线性表既要快速查找又“经常”动态变化,则可采用分块查找。
二叉排序树
定义:
二叉排序树或是空树,或是满足如下性质的二叉树:
(1)若其左子树非空,则左子树上所有结点的值均小于根结点的值;
(2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
(3)其左右子树本身又各是一棵二叉排序树
算法思路:
p最初指向根结点,只要p不空且p所指结点不是所求则根据比较结果令p变为当前
结点的左孩子或右孩子。如此重复则最终p空或者找到
typedef struct{ KeyType key; …; } ElemType;
typedef struct BiTNode{
ElemType data;
struct BiTNode;
*lchild,*rchild;
}BiTNode, *BiTree;
typedef BiTree BSTree;
typedef BiTNode BSTNode;
BiTNode *Search(BSTree T, KeyType key){
BiTNode *p=T;
while(p!=null&&!EQ(key.p->data.key)){
if(LT(key.p->data.key)) p=p->lchild;
else p=p->rchild;
}
return p;
}//非递归实现
二叉排序树的操作-递归
*若为空,则返回NULL;
*不空时:
若查找的关键字等于根结点,返回T
否则
若小于根,查其左子树,返回左子树中的查找结果
若大于根,查其右子树,返回右子树中的查找结果
代码描述:
BSTree SearchBST(BSTree T,KeyType key) {
if((!T) return NULL;
else {
if(key==T->data.key) return T;
else if (key<T->data.key)
return SearchBST(T->lchild,key);
else return SearchBST(T->rchild,key);
}
} // SearchBST
插入:
若二叉排序树为空,则插入结点应为根结点
否则,在已有二叉排序树中查找是否存在相等结点
若树中已有,不再插入
若树中没有,则查找过程中必然会“落空”。该“落空”的位置就是新结点应该
在的位置
代码描述:
Status InsertBST(BSTree &T, ElemType e)
{
if((!T){
s=new BiTNode;
s->data=e;
s->lchild=s->rchild=NULL;
T=s; return OK;
}
else if(key==T->data.key)
return ERROR;
else if (key < T->data.key)
return InsertBST(T->lchild,key);
else
return InsertBST(T->lchild,key);
} //考虑非递归如何实现?
创建:
Status CreateBST(BSTree &T){
T=NULL;
InputElem(e);
while(!IsEnd(e)){
if(InsertBST(T,e)==ERROR)
return ERROR;
else InputElem(e);
}
}
删除:
//首先定位被删除的结点及其双亲
//如果不存在关键字等于key的结点则直接返回
//否则,根据被删节点特性分类处理,保证删后仍是二叉排序树
//若是叶子节点?
//若只有左孩子?
//若只有右孩子?
//若有两个孩子?
void DeleteBST(BiTree &T,KeyType key){
p=T; f=NULL;
while(p!=NULL&&!EQ(key,p->data.key)){
if(LT(key,p->data.key)) { f=p; p=p->lchild; }
else { f=p; p=p->rchild; }
}
if(!p) return;
}
(1)被删除的结点是叶子结点
若有双亲则将双亲结点中相应指针域的值置“空”. 否则,T置空
if( !p->lchild&&!p->rchild ){
if(!f) { free(p); p=NULL; T=NULL;
else if(f->lchild==p){ free(p); f->lchild=NULL; }
else {free(p); f->rchild=NULL;}
}
(2)被删结点只有左子树或右子树
双亲结点相应指针域指向被删结点的唯一子树
if(!p->lchild){//左子树为空而只有右子树的情况
if(!f) {T=p->rchild; free(p);}
else if(f->lchild==p){f->lchild=p->rchild; free(p);}
else {f->rchild=p->rchild; free(p);}
}//只有左子树类似处理
(3)被删结点基友左子树也有右子树
找左子树最大元素代替被删除元素,归结为前一情况(还可…)
if(p->lchild&&p->rchild){
f=p; q=p->lchild; //f始终指向q的双亲,方便后面的删除
while(q->rchild){f=q; q=q->rchild;} //q定位最大,f指向其双亲
p->data=q->data; //左子树最大者的值填充到原本被删节点
if(f->lchild==q) f->lchild = q->lchild; //删除左子树中最大者
else f->rchild=q->lchild;
free(q);
}
代码实现:
void DeleteBST(BiTree &T,KeyType key){
//首先定位被删结点及其双亲, 不存在则直接返回
p=T; f=NULL;
while(p!=NULL&&!EQ(key,p->data.key)){
if(LT(key,p->data.key)){f=p;p=p->lchild;}else{f=p; p=p->rchild;}
}
if(!p) return;
if(p->lchild&&p->rchild){//若被删节点有两个孩子
f=p; q=p->lchild; //f始终指向q的双亲,方便后面的删除
while(q->rchild){f=q; q=q->rchild;} //q定位最大,f指向其双亲
p->data=q->data; //左子树最大者的值填充到原本被删节点
if(f->lchild==q) f->lchild = q->lchild; else f->rchild=q->lchild;
free(q);
}
else if(!p->lchild){//若左孩子为空(包括只有右孩子或两个孩子都没有的情况)
if(!f) {free(p); T=p->rchild;}
else if(f->lchild==p){f->lchild=p->rchild; free(p);}
else {f->rchild=p->rchild; free(p);}
}
else {…} //只有左孩子,仿照上一段代码, 绿色部分换成p->lchild
一、查找的基本概念
查找表;可以看作一个新的ADT工具类,数据对象是候选记录的集合,核心操作是
查找
静态查找表:一旦创建,主要是查找,很少或者不进行记录的插入和删除
动态查找表:查找的同时会对查找表进行插入、删除
关键字:记录中某个数据项的值,可用来识别一个记录
主关键字:唯一标识数据元素
次关键字;可以标识若干个数据元素
二、静态查找表的查找
定义:
typedef *** KeyType;//关键字类型
typedef struct Search{
KeyType key;
...;
}ElemType;//记录类型
typedef struct {
ElemType *elem;//数组首地址
int length;//表长,有效记录
}SSTable;
静态查找表上的顺序查找:
//添加哨兵,从后向前比较,省略每次循环事的越界检查
int Seach_Seq(SSTable ST,KeyType key)
{
ST.elem[0].key=x;
for(i=ST.length;St.elem[i].key!=x;--i)
return i;
}
*记第i个元素被查找的概率为Pi, 顺序查找平均查找长度:ASLSS=n*P1+(n-1)*P2+..+Pi*(n-i+1)+..+1*Pn +(n+1)*P0
*通常所查找元素均在ST中,此时P0=0
*若查找保证成功且各元素几率同(1/n)则ASLSS=(n+1)/2
*若成功与否各50%,且各元素被找到概率同(1/2n)则
ASLSS=(n+1)/4 + (n+1)/2= 3*(n+1)/4
*概率越大越靠后放则ASL越小,事先不知概率可动态调整
折半查找
int Search_Bin(SSTable ST,
KeyType key){
low=1; high=ST.length;
while (low <= high) {
mid = (low + high) / 2;
if (key=ST.elem[mid].key)
return mid;
else if(key<ST.elem[mid].key)
high=mid-1;
else
low = mid + 1;
}
return 0;
}
折半查找树
*折半查找的判定树:比较时生成根,到左侧区间比较时生成左孩子,右侧比较生成右
孩子,如此递归。
*折半查找判定树任意节点左右子树深度相差最多为1,可证此类树深度为[log2n]+1,
籍此可证查找成功时最坏查找长度为[log2n]+1,借助方形外部结点可证不成功时的
最大查找长度也如此。
*折半查找判定树特点:右子树中各结点值比根大,左子树中各结点值比根小,子树
也如此.根据判定树可如下完成查找:从根结点开始,比当前元素大就向右、小则
向左,或者找到,或者到空.
*最坏查找长度为[log2n]+1,复杂度O(log2n)
*当判定树为满二叉树时(n=2h-1),若查找成功且各元素几率同(1/n)则ASLbs=(1*20+2*21+3*22+…+h*2h-1)/n≈log2(n+1)-1
对具体例子要会求查找成功和不成功的平均查找长度
分块查找
定义:
对数据进行分块,使得块内无序、块间有序。将各块中的最大关键字构成一个索
引表,表中还要包含每块的起始地址。
过程:
① 对索引表用折半查找定位最后一个小于等于key的块
② 确定了待查关键字所在的块后,块内采用顺序查找
优缺点:
优点:删除或者插入相对容易,移动记录量变小。
缺点:要增加一个索引表的存储空间并对初始索引表进行排序运算。
适用情况:如果线性表既要快速查找又“经常”动态变化,则可采用分块查找。
二叉排序树
定义:
二叉排序树或是空树,或是满足如下性质的二叉树:
(1)若其左子树非空,则左子树上所有结点的值均小于根结点的值;
(2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
(3)其左右子树本身又各是一棵二叉排序树
算法思路:
p最初指向根结点,只要p不空且p所指结点不是所求则根据比较结果令p变为当前
结点的左孩子或右孩子。如此重复则最终p空或者找到
typedef struct{ KeyType key; …; } ElemType;
typedef struct BiTNode{
ElemType data;
struct BiTNode;
*lchild,*rchild;
}BiTNode, *BiTree;
typedef BiTree BSTree;
typedef BiTNode BSTNode;
BiTNode *Search(BSTree T, KeyType key){
BiTNode *p=T;
while(p!=null&&!EQ(key.p->data.key)){
if(LT(key.p->data.key)) p=p->lchild;
else p=p->rchild;
}
return p;
}//非递归实现
二叉排序树的操作-递归
*若为空,则返回NULL;
*不空时:
若查找的关键字等于根结点,返回T
否则
若小于根,查其左子树,返回左子树中的查找结果
若大于根,查其右子树,返回右子树中的查找结果
代码描述:
BSTree SearchBST(BSTree T,KeyType key) {
if((!T) return NULL;
else {
if(key==T->data.key) return T;
else if (key<T->data.key)
return SearchBST(T->lchild,key);
else return SearchBST(T->rchild,key);
}
} // SearchBST
插入:
若二叉排序树为空,则插入结点应为根结点
否则,在已有二叉排序树中查找是否存在相等结点
若树中已有,不再插入
若树中没有,则查找过程中必然会“落空”。该“落空”的位置就是新结点应该
在的位置
代码描述:
Status InsertBST(BSTree &T, ElemType e)
{
if((!T){
s=new BiTNode;
s->data=e;
s->lchild=s->rchild=NULL;
T=s; return OK;
}
else if(key==T->data.key)
return ERROR;
else if (key < T->data.key)
return InsertBST(T->lchild,key);
else
return InsertBST(T->lchild,key);
} //考虑非递归如何实现?
创建:
Status CreateBST(BSTree &T){
T=NULL;
InputElem(e);
while(!IsEnd(e)){
if(InsertBST(T,e)==ERROR)
return ERROR;
else InputElem(e);
}
}
删除:
//首先定位被删除的结点及其双亲
//如果不存在关键字等于key的结点则直接返回
//否则,根据被删节点特性分类处理,保证删后仍是二叉排序树
//若是叶子节点?
//若只有左孩子?
//若只有右孩子?
//若有两个孩子?
void DeleteBST(BiTree &T,KeyType key){
p=T; f=NULL;
while(p!=NULL&&!EQ(key,p->data.key)){
if(LT(key,p->data.key)) { f=p; p=p->lchild; }
else { f=p; p=p->rchild; }
}
if(!p) return;
}
(1)被删除的结点是叶子结点
若有双亲则将双亲结点中相应指针域的值置“空”. 否则,T置空
if( !p->lchild&&!p->rchild ){
if(!f) { free(p); p=NULL; T=NULL;
else if(f->lchild==p){ free(p); f->lchild=NULL; }
else {free(p); f->rchild=NULL;}
}
(2)被删结点只有左子树或右子树
双亲结点相应指针域指向被删结点的唯一子树
if(!p->lchild){//左子树为空而只有右子树的情况
if(!f) {T=p->rchild; free(p);}
else if(f->lchild==p){f->lchild=p->rchild; free(p);}
else {f->rchild=p->rchild; free(p);}
}//只有左子树类似处理
(3)被删结点基友左子树也有右子树
找左子树最大元素代替被删除元素,归结为前一情况(还可…)
if(p->lchild&&p->rchild){
f=p; q=p->lchild; //f始终指向q的双亲,方便后面的删除
while(q->rchild){f=q; q=q->rchild;} //q定位最大,f指向其双亲
p->data=q->data; //左子树最大者的值填充到原本被删节点
if(f->lchild==q) f->lchild = q->lchild; //删除左子树中最大者
else f->rchild=q->lchild;
free(q);
}
代码实现:
void DeleteBST(BiTree &T,KeyType key){
//首先定位被删结点及其双亲, 不存在则直接返回
p=T; f=NULL;
while(p!=NULL&&!EQ(key,p->data.key)){
if(LT(key,p->data.key)){f=p;p=p->lchild;}else{f=p; p=p->rchild;}
}
if(!p) return;
if(p->lchild&&p->rchild){//若被删节点有两个孩子
f=p; q=p->lchild; //f始终指向q的双亲,方便后面的删除
while(q->rchild){f=q; q=q->rchild;} //q定位最大,f指向其双亲
p->data=q->data; //左子树最大者的值填充到原本被删节点
if(f->lchild==q) f->lchild = q->lchild; else f->rchild=q->lchild;
free(q);
}
else if(!p->lchild){//若左孩子为空(包括只有右孩子或两个孩子都没有的情况)
if(!f) {free(p); T=p->rchild;}
else if(f->lchild==p){f->lchild=p->rchild; free(p);}
else {f->rchild=p->rchild; free(p);}
}
else {…} //只有左孩子,仿照上一段代码, 绿色部分换成p->lchild