查找
注:参考严老师的数据结构c实现版,自复习用
总述
- 查找表
- 静态查找表、动态查找表
- 关键字、主关键字、次关键字
- 查找、查找成功、查找不成功
- 查找中通常以“其关键字和给定值进行过比较的记录的个数的平均值”作为衡量查找算法的好坏。
关键字说明以及数据元素类型统一说明:
-
典型关键字类型说明:
typedef float KeyType; //实型 typedef int Keytype; typedef char *KeyType;
-
数据元素类型定义:
typedef struct{ KeyType key;//关键字 //…… }
-
对两个关键字的比较约定为如下宏定义:
//------对数值型关键字 # define EQ(a,b) ((a) == (b)) # define LT(a,b) ((a) < (b)) # define LQ(a,b) ((a)<=(b)) //……………… //-------对字符串型关键字 # define EQ(a,b) (!strcmp((a),(b))) //…………
一.静态查找表
1.1 顺序表的查找
- 零号单元赋为key值作为“哨兵”,从后往前若查找不成功则返回0代表false
- 平均查找长度Average Search Length:
A S L = ∑ i = 1 n P i C i ASL=\sum_{i=1}^nP_iC_i ASL=∑i=1nPiCi - 若概率不想等时,则概率大的记录放顺序表后面。
- 访问频度排序
- 当查找不成功的情况不能被忽视时,算法的平均长度为成功与不成功的平均查找长度之和。
- 顺序表中查找不成功的比较次数为n+1
1.2 有序表的查找
1.2.1折半查找
- 不成功时的比较关键字的次数最多不超过 ⌊ l o g n ⌋ + 1 \lfloor{logn}\rfloor+1 ⌊logn⌋+1
- 平均查找长度: A S L b s = 1 n ∑ j = 1 h j 2 j − 1 ASL_{bs}=\frac{1}{n}\sum_{j=1}^{h}j2^{j-1} ASLbs=n1∑j=1hj2j−1
- 斐波那契查找、插值查找
1.3 静态树表查找
1.3.1 二叉查找
- 路径长度之和PH值最小: P H = ∑ i = 1 n w i h i PH=\sum_{i=1}^nw_ih_i PH=∑i=1nwihi
- 静态最优查找树、次优查找树
1.4 索引顺序表的查找
1.4.1 分块查找
- 索引有序,而块中的记录任意排序
- 长度为n的线性表,其时间复杂度为O(n1/2)
设每块含m个元素,则共分为 n m \frac{n}{m} mn块,则需比较 m + n m m+\frac{n}{m} m+mn次
二. 动态查找表
2.1 二叉排序树和平衡二叉树
2.1.1 二叉排序树
- 若查找不成功则需要返回最后不成功的结点的位置在该位置上插入查找值
- 删除时以直接前驱/后继取代之
- ASL=O(logn)
2.1.2 AVL平衡树
定义
- 左右子树都是平衡树(包括空),且左右子树高度差绝对值不超过1
- 结点平衡因子BF:结点左子树与右子树之差,只能是-1,0,+1
- 插入结点后,对最小不平衡经过平衡旋转处理之后的子树深度和插入之前相同,故不影响插入路径上所有祖先结点的平衡度。
- 旋转处理后,要将根节点和相应子树根节点平衡因子改为0
- 查找时间复杂度为O(logn)
在平衡的二叉排序树BBST上插入新数据元素e的递归算法
# define LH 1 //左高
# define EH 0 //等高
# define RH -1 //右高
typedef struct BSTNode{
ElemType data;
int bf; //平衡因子
struct BSTNode *lchild,*rchild;
}BSTNode;
Status InsertAVL(BSTNode &T,ElemType e,bool &taller){
//taller表示树长高与否
if(!T)//插入新节点,树“长高”,置taller为true
{
taller=true;
T = new BSTNode;
T->data=e;
T->lchild=T->rchild=null;
T->bf=EH;
}//if:T为空
else
{
if(EQ(T->data,e)){taller=false;return 0;}//树中存在和e有相同关键字的结点则不再插入,树没有长高。
if(LT(T->data,e))//应在左子树上进行搜索
{
if(!InsertAVL(T->lchild,e,taller)){taller=false;return 0;}//未插入
if(taller)//已插入到T的左子树上且左子树长高
{
switch(T->bf)//检查T的平衡度
{
case LH: //原本左子树较右子树高,现在左子树又长高,需要做平衡处理
L_Rotation(T);
taller=false; //对该结点平衡处理后,树没有长高。
break;
case EH: //原本左右子树等高
T->bf=LH;
taller=true;
break;
case RH:
T->bf=EH;
taller=false;
break;
}//switch
}//if ltaller
}//if left
else//否则在右子树上进行搜索
{
if(!InsertAVL(T-.rchild,e,taller)) {taller=false;return 0;}//没有进行插入
if(taller)//插入右子树并且右子树长高了
{
switch(T->bf)
{
case LH:
T->bf=EH;
taller=false;
break;
case EH:
T->bf=RH;
taller=true;
break;
case RH:
R_Rotation(T);
taller=false;
break;
}//switch
}//if rtaller
}//right
}//else 树不为空
return 1;//插入成功
}//InsertAVL
void L_Rotation(BSTNode &T)//平衡左子树
{
BSTNode *lc=T->lchild;
if(lc->bf==LH)//做LL旋转。因为新节点的插入使左子树长高了,故较高一边的子树就是结点插入的子树
{
BSTNode *gc=lc->lchild;
T->lchild=lc->rchild;
lc->rchild=T;
T->bf=EH;//因本来左子树比右子树高2,T的左子树的右子树与T的右子树等高。
T=lc;
}//if
else//LR旋转
{
BSTNode *gc=lc->rchild;
lc->rchild=gc->lchild;
T->lchild=gc->rchild;
gc->lchild=lc;
gc->rchild=T;
switch(gc->bf)//经推导,设T的右子树高度为h,则gc的左右子树中,一定有一个高度为h,另一个高度为h-1,不确定是左子树还是右子树了故修改T左孩子(接受gc左孩子)和T(接受gc右孩子)的平衡因子
{
case LH://说明gc的左子树高度为h,右子树高度为h-1
lc->bf=EH;
T->bf=RH;
break;
case EH:
T->bf=lc->bf=EH;
break;
case RH:
lc->bf=LH;
T->bf=EH;
break;
}//switch
T=gc;
T->bf=EH;
}//LR旋转
}//L_Rotation
R-L、R-R对称。
2.2 B-树
-
m阶B-树,除了根节点之外,所有非终端结点至少有 ⌈ m 2 ⌉ \lceil{\frac{m}{2}\rceil} ⌈2m⌉棵子树
-
包括根节点,至多有m棵子树。
-
若根节点不是叶子节点,则至少有两棵子树
-
节点类型
-
所有的叶子节点都出现在同一层次上,并且不带信息
#define m 3 typedef struct BTNode{ int keynum; //结点中关键字的个数,即结点的大小 struct BTNode *parent; //指向双亲的指针 KeyType k[m+1]; //关键字向量,0号单元未用。 structural BTNode *ptr[m+1]; //子树指针向量。 Record *record[m+1]; //记录指针向量,0号单元未使用。 }BTNode,*BTree;
-
在磁盘上查找的次数,即B-树的层数为首要因素。
-
B-树的插入、删除
-
在高度为h的B-树中,叶子节点处于第h+1层,当向该树中插入新关键码时,为查找插入位置需读取h个结点。
-
对于包含n个关键码的m阶B树,其最小高度满足:
n = m h − 1 m − 1 ( m − 1 ) n=\frac{m^h-1}{m-1}(m-1) n=m−1mh−1(m−1)
即
h m i n = l o g m ( n + 1 ) h_{min}=log_{m}(n+1) hmin=logm(n+1)
最大高度:
由B-树的性质,可知其叶子层(即第h+1层)的结点数为n+1
递推B-树最高时,每一层的结点数:
层数h | 结点个数 |
---|---|
1 | 1 |
2 | 2 |
3 | 2* ⌈ m 2 ⌉ \lceil{\frac{m}{2}\rceil} ⌈2m⌉ |
4 | 2* ⌈ m 2 ⌉ 2 \lceil{\frac{m}{2}\rceil}^2 ⌈2m⌉2 |
…… | …… |
h+1 | 2* ⌈ m 2 ⌉ h − 1 \lceil{\frac{m}{2}\rceil}^{h-1} ⌈2m⌉h−1 |
则有2*
⌈
m
2
⌉
h
−
1
=
n
+
1
\lceil{\frac{m}{2}\rceil}^{h-1}=n+1
⌈2m⌉h−1=n+1
得最大高度
h
=
l
o
g
⌈
m
2
⌉
(
n
+
1
2
)
+
1
h=log_{\lceil\frac{m}{2}\rceil}(\frac{n+1}{2})+1
h=log⌈2m⌉(2n+1)+1
2.3 键树
- 只有叶子节点存储记录
2.4 哈希表
- 成功查找平均长度: A S L s ASL_{s} ASLs查找到散列表中已存在的结点的平均比较次数
- 失败查找平均长度: A S L u ASL_{u} ASLu查找失败,但找到插入位置的平均比较次数