1
第九章 查找
? 静态查找表静态查找表
? 动态查找表动态查找表
? 哈希查找表哈希查找表
数
据
结
构
之
查
找
2
9.1 基本概念 (Page 214)
?查找表:是由同一类型数据元素构成的集合。
?静态查找表:只做“查询”或“检索”操作。
?动态查找表:查询、检索、插入、删除。
?关键字:是数据元素中某个数据相的值,用
它可以标识一个数据元素。
主关键字、次关键字
?查找:根据给定值,在查找表中确定一个其
关键字等于给定值的数据元素。
查找成功返回位置序号、查找不成功返回0
2
数
据
结
构
之
查
找
3
注意
比较各种查找算法时间
复杂度和空间复杂度,
查找算法的主要时间用
于“关键字的比较”。
数
据
结
构
之
查
找
4
9.2 静态查找表
?顺序表的查找
typedef int KT ;
typedef struct{KT key ; ... }ET;
typedef struct{ET *elem; //数据元素存储空间基址,
0为空单元
int length;}SST;
int Search_Sq(SST ST, KT key){
St.elem[0].key=key;//设置哨兵
for(i=ST.length ; key!=ST.elem[i].key; i--) ;
return i;
}
3
数
据
结
构
之
查
找
5
?顺序表的查找算法性能分析
在等概率的情况下:Pi=1/n
?查找成功时的平均查找长度:(n+1)/2
?查找不成功时的比较次数:n+1
?假设查找成功与不成功的可能性相同,
在等概率的情况下:Pi=1/2n , 则顺序
查找的平均查找长度为:
ASLss=((n+1)+(n+1)/2)/2=3(n+1)/4
数
据
结
构
之
查
找
6
?有序表的查找——折半查找
?折半查找(二分查找):经过一次比较将
表分割成两部分,然后只在表的一部分中
继续进行查找的方法。
mid=(low+high)/2
key =13
4
数
据
结
构
之
查
找
7
?二分算法描述
int Search_Bin(SST ST, KT 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;
}//时间复杂度:O(log2 n)
数
据
结
构
之
查
找
8
?二叉树的性质
1、在二叉树的第i 层上至多有2 个结点(i>=1);
2、深度为k的二叉树至多有2 -1个结点(k>=1);
3、对任何一棵二叉树T,如果其终端结点数为
n0,度为2的结点数为n2,则n0=n2+1。
4、具有n个结点的完全二叉树的深度为:
?log2 n? +1
k
i-1
5
数
据
结
构
之
查
找
9
?二叉判定树
?查找过程可用二
叉树来描述。树
中每个结点表示
一个记录,结点
中的值为该记录
在表中的位置,
通常这个描述查
找过程的二叉树
为判定树。
?在该树中,和给
定值进行比较的
次数恰为该结点
在判定树中的层
次。
数
据
结
构
之
查
找
10
?性能分析
比较次数最多:
?log2 n?+1
不超过判定树的深度
时间复杂度:
O(log2 n)
?此方法只能适用
于有序表,且限
于顺序存储结构。
6
数
据
结
构
之
查
找
11
?索引顺序表的查找(分块查找)要求:
?索引表
?按表中记录的关键字分块, R
1
,R
2
,…,R
L
第R
k
块中所有关键字< R
k+1
块中的所有关键字
k=1,2,…,L-1, 称为“分块有序”
?对每块建立一个索引项, 包含有两项内
容:
?关键字项: 为该块中最大关键字值;
?指针项: 为该块第一个记录在表中位置.
?所有索引项组成索引表
数
据
结
构
之
查
找
12
?查找过程
?确定待查记录所在块; (可以用顺序或折半查
找)
?在块内顺序查找. (块内查找只能用顺序查找)
22 1 48 7 86 13
22 12 13 8 9 20 33 42 44 38 24 48 60 58 74 49 86 53
12345678910112 13 14 15 16 17 18
索引表
查找数据24
7
数
据
结
构
之
查
找
13
?性能分析
ASL
bs
=L
b
+L
w,
L
b
为确定块的平均查找长度;L
w
为块内查找次数。
若顺序查找:ASL
bs
=L
b
+L
w
=(n/s+s)/2+1
若折半查找:ASL
bs
=L
b
+L
w
= log
2
(n/s+1)+s/2
三种查找方法比较三种查找方法比较
顺序查找顺序查找折半查找折半查找
分块查找分块查找
ASL大大小小中中
表结构要求表结构要求
无无有序表有序表
块之间有序块之间有序
数
据
结
构
之
查
找
14
9.3 动态查找表
?二叉排序树(BST)
?二叉排序树的定义:page 227
?查找过程
?二叉排序树的插入和删除
?二叉排序树的查找分析
含有n个结点的二叉排序树的平均查找长度
和树的形态有关:最差的形态是:单支树
( (n+1)/2 );最好的形态是:判定树( log
2
n )。
在随机情况下,二叉排序树的平均查找长度
和log n是等数量级的。
8
数
据
结
构
之
查
找
15
数
据
结
构
之
查
找
16
?查找算法
?当前BST非空时,将给定值k与当前根结
点的关键字比较:
?若相等,查找成功,结束; 若k小于当前根
结点的关键字,则将左子树作为当前BST;
若k大于当前根结点的关键字,则将右子
树作为当前BST;
?重复(1)。
9
数
据
结
构
之
查
找
17
BT SearchBST ( BT T,int key )
{
if ( ( !T ) || key = =T->data) )
return ( T );
else if (key<T->data)
return ( SearchBST ( T->lchild, key ) );
else
return ( SearchBST ( T->rchild, key ) );
}
数
据
结
构
之
查
找
18
Status Search_Bit(BT T,BTN &f ,BTN &p, int
key){
// f 是p 的双亲结点指针
p=T; f=T ;
while(p){
if(p->data==key) return TRUE;
else if(key<(p->data)){ f = p ; p=p->lchild; }
else {f = p ; p=p->rchild ; }
}
return FALSE; }
10
数
据
结
构
之
查
找
19
?在二叉排序树中插入关键字为key 的结点
Status Insert_Bit(BT T ,int key ){
if( !Search_Bit(T , f , p , key )){
p=(BTN *)malloc(sizeof(BTN));
p->lchild=p->rchild= NULL;
p->data = key ;
if(!f ) T= p;
else if (key<(f->data) ) f->lchild=p;
else f->rchild=p;
return OK; }
return ERROR ;
}
数
据
结
构
之
查
找
20
例: 将序列(45,24,53,12,24,90)
构造成为二叉排序树。
11
数
据
结
构
之
查
找
21
? BST的特点:
?中序遍历BST可得到一个关键字的有序
序列。
?在BST上插入新结点时,不必移动其他
结点,仅需改动某结点的指针(因新结点
总是BST上的一个新叶结点)。
? BST既具有类似折半查找的特性(与BST
的平衡度有关)又采用了链表存储结构
(折半查找不能为链表存储结构),因此,
对于经常要进行查找、插入和删除记录的
有序表,采用BST尤其合适。
数
据
结
构
之
查
找
22
?平衡二叉树(AVL树):
?定义:它或是一棵空树,或者是左子树
和右子树的深度之差的绝对值不超过1;
且左子树和右子树都是平衡二叉树。
?平衡因子BF:是该结点的左子树的深度
减去它的右子树的深度。
?平衡二叉树上的所有结点的平衡因子只
可能是-1,0,1。
?在构成二叉排序树的过程中进行“平衡
化”处理,成为二叉平衡树
12
数
据
结
构
之
查
找
23
例:平衡二叉树与非平衡二叉树的比较。
平衡二叉树
非平衡二叉树
数
据
结
构
之
查
找
24
二叉平衡树的平衡调整的四种基本类型
L
L
型
R
R
型
插入
元素
1
插入
元素
9
13
数
据
结
构
之
查
找
25
L
R
型
R
L
型
数
据
结
构
之
查
找
26
LL型平衡旋转(顺时针)型平衡旋转(顺时针)
失去平衡点的平衡因子为失去平衡点的平衡因子为2, 其左孩子的平衡因子为其左孩子的平衡因子为1
LL
0
A
A
R
B
R
h-1h-1
0
B
B
L
h h
2
A
a
A
R
h-1
1
B
B
R
B
L
h-1h
h+1
L
+1
14
数
据
结
构
之
查
找
27
RR型平衡旋转(逆时针,与型平衡旋转(逆时针,与LL对称对称)
失去平衡点的平衡因子为失去平衡点的平衡因子为-2, 其右孩子的平衡因子为其右孩子的平衡因子为-1
-2
A
A
L
B
L
h-1
h-1
-1
B
h
B
R
RR
0
B
B
L
A
L h-1
0
A
B
R
数
据
结
构
之
查
找
28
LR型平衡旋转型平衡旋转
失去平衡点的平衡因子为失去平衡点的平衡因子为2, 其左孩子的平衡因子为其左孩子的平衡因子为-1
2
A
A
R
h-1
-1
B
B
L
C
L
h-1
h-1
h+1
1
C
h
h-2
C
R
LR
0
C
A
R
h-1
0
B
B
L
C
L
h-1
h-1
-1
A
h-2
C
R
中序遍历:中序遍历:B
L
B C
L
C C
R
A A
R
B
L
B C
L
C C
R
A A
R
15
数
据
结
构
之
查
找
29
RL型平衡旋转型平衡旋转
失去平衡点的平衡因子为失去平衡点的平衡因子为-2, 其右孩子的平衡因子为其右孩子的平衡因子为1
RL
0
C
B
R
0
A
A
L
C
L
-1
B
h-2
C
R
1
B
B
R
h-1
-2
A
A
L h-1
C
L
h-1
1
C
h-2
C
R
中序遍历:中序遍历:A
L
A C
L
C C
R
B B
R
A
L
A C
L
C C
R
B B
R
数
据
结
构
之
查
找
30
?平衡化算法
?平衡二叉排序树的类型定义:
typedef struct BSTN{
Elemtype data;
int bf; /* 结点的平衡因子*/
struct BSTN *Lc , *Rc; } BSTN ,*BST;
1. 右旋处理:p指向旋转处理之前的左子树的根结点
void R_rotate( BST &p){ lc=p->Lc ;
p->Lc=lc->Rc ; lc->Rc=p ; p=lc;}
2. 左旋处理:p指向旋转处理之前的右子树的根结点
void L_rotate(BST &p) { rc=p->Rc ;
p->Rc= rc->Lc ; rc->Lc=p ; p=rc ; }
A
B
p
lc A
B
p
rc
16
数
据
结
构
之
查
找
31
?对二叉树T作左平衡旋转
void LeftBalance( BST &T){ lc =T->Lc;
switch(lc->bf){
case 1: T->bf = lc->bf =0;
R_rotate(T); break;
case -1: rd = lc->Rc;
switch(rd->bf){
case 1:T->bf = -1 ; lc->bf =0;break;
case 0:T->bf = lc->bf =0;break;
case -1:T->bf =0;lc->bf =1; }
rd->bf =0;
L_rotate(T->Lc); R_rotate(T); } }
数
据
结
构
之
查
找
32
?在平衡二叉排序树上插入元素
Status InsertAVL(BST &T, ElemType e ,Boolean &taller)
{ if(!T) { T=(BST)malloc(sizeof(BSTN)); T->data=e;
T->Lc=T->Rc=NULL;T->bf =0;taller =TRUE; }
else { if( e.key==T->data.key){taller=FALSE;return 0;}
if((e.key<T->data.key){k =InsertAVL(T->Lc,e,taller);
if(!k) return 0;
if(taller) switch(T->bf){
case 1: LeftBalance(T);taller =FALSE;break;
case 0: T->bf =1;taller =TRUE; break;
case -1: T->bf =0;taller =FALSE; } }
else {k = InsertAVL(T->Rc , e , taller );
17
数
据
结
构
之
查
找
33
if(!k) return 0;
if(taller)
switch(T->bf){
case 1: T->bf =0 ;
taller=FALSE;break;
case 0:T->bf = -1 ;
taller=TRUE ; break;
case -1: RightBalace(T) ;
taller = FALSE ; } }
}
return 1;
}
数
据
结
构
之
查
找
34
7.4 B-树
? B-树的定义:一棵m阶B-树,或为空树,或
为满足下列特性的m叉树:
?树中每个结点至多有m棵子树;
?若根结点不是叶子结点,则至少有两棵子树;
?除根之外的所有非终端结点至少有?m/2?棵子树;
?所有的结点中包含下列信息数据(P 239)
( n , A0 , K1 , A1 , K2 , A2 ,......, Kn , An ) (Ki<Ki+1)
?所有的叶子结点都出现在同一层次上。
例如:3阶B-树的非终端结点:
N A0 Key1 A1 Key2 A2
18
数
据
结
构
之
查
找
35
一棵四阶的B
-
树
1 11
351
1 18
1 27 1 9964533 47
78432
1 39
F FF F F FFFF FFF
数
据
结
构
之
查
找
36
B-树的查找
3 阶B-树
30
25 48 32 , 38
50
45
57
Key = 38
t
19
数
据
结
构
之
查
找
37
? B 树的插入
在B 树中插入时,形成的结点‘分裂’是
由下层结点逐步向上层传递的,在极端
情况下,会一直传递到根结点(实际上,
这是B 树长高的唯一途径)。
38 , 45 , 57
45
38 57
45 , 57
结点分裂
插
入
38
依次插入48,25,30
数
据
结
构
之
查
找
38
45
25 , 30 , 38 48 , 57
30 , 45
25
48 , 50 , 57
38
30
25 48 32 , 38
50
45
57
试依次插入32,15,55,35,20,41,90
插入50,
产生两次
结点分裂
20
数
据
结
构
之
查
找
39
B
-
树
的
删
除
操
作
?可能形成结
点的合并
? B-树的高度
降低。30
25 48 32 , 38
50
45
57
32
25 48 38
50
45
57
32 , 45
25
48 , 57
38
1.删除关键字30
2.删除关键字50
数
据
结
构
之
查
找
40
7.5 哈希表
?概念
?哈希函数:在记录的存储位置和它的关键字之
间建立一个确定关系f ,是每个关键字和结构
中一个唯一的存储位置对应。这个对应关系f
为哈希函数。根据这个思想建立的表为哈希表。
?同义词:若key1 != key2 ,而f(key1)=f(key2),
则这种现象成为冲突,且key1和key2对哈希
函数f 来说称为同义词。
关键字集A
m
地址空间D
n
哈希函数哈希函数
H((k))
21
数
据
结
构
之
查
找
41
?哈希造表或散列:根据设定的哈希函数f
=H(key)和处理冲突的方法,将一组关键
字映象到一个有限的连续地址集(区间)
上,以关键字的哈希函数值作为记录在表
中的位置,这一映象过程称为哈希造表或
散列。记录的存储位置叫哈希地址或散列
地址
?关键问题
?构造好哈希函数的方法;
?研究解决冲突的方法。
数
据
结
构
之
查
找
42
?装填因子:
表中填入的记录数
α =
哈希表的长度
α标志哈希表的装满程度:
α越小,发生冲突的可能性就越小;反之,
α越大,表中已填入的记录越多,再填记
录时,发生冲突的可能性就越大。
22
数
据
结
构
之
查
找
43
?哈希函数的构造方法
?判定标准
?计算简单容易
?冲突极少
?直接地址法:取关键字或关键字的某个线性
函数值为哈希地址。
H(key)=key 或H(key)=a*key+b
例:开辟60个记录的存储区,key =年号
H(key)=key -1949哈希地址
H(1949)= 0 H(1962)= 13
H(1950)= 1 H(1963)= 14
H(1951)= 2 H(1964)= 15
.............. H(1998)= 49
数
据
结
构
之
查
找
44
?除留余数法:取关键字被某个不大于哈希表表
长m的数p除后所得余数为哈希地址。
H(key)=key mod p ( p ≤ m)
例:开辟38 个记录的存储区,
key = 学号% 1000
H(key)=key % 38
H(031)= 31 H(044)= 6
H(032)=32 H(045)= 7
H(033)= 33 H(046)= 8
.............. H(069)= 31
H(071)=33 //031和069是同义词,散列时产生冲突
通常,选择p≤m的某个质数。
23
数
据
结
构
之
查
找
45
?数字分析法
假设关键字是以r 为基的数,并且哈希表中可能出现的关键
字都是事先知道的,则可取关键字的若干数位组成哈希地址。
?平方取中法
取关键字平方后的中间几位为哈希地址。
?折叠法
将关键字分割成位数相同的几部分,然后取这几部分的叠加
和。
?随机数法
选择一个随机函数,取关键字的随机函数值为它的哈希地址。
即:H(key)=random(key)。
数
据
结
构
之
查
找
46
?处理冲突的方法
?开放地址法:(探测法)
Hi=(H(key)+di) mod m
(1)线性探测再散列:di=1,2,3,4,.....,m-1
(2)二次探测再散列:di=±k*k ( 1 ≤ k ≤ m/2)
(3)伪随机探测再散列:di=伪随机数列
?再哈希法:用另一个哈希函数计算地址,直到
冲突不再发生。
?链地址法:将所有的关键字为同义词的记录存
储在同一线性链表中。(Page 258)
?建立一个公共溢出区:Hash表分为基本表和
溢出表,所有发生冲突的关键字都填入溢出表。
2
24
数
据
结
构
之
查
找
47
例: 长度为16的哈希表中已有关键字节19,70,33,
H(key)= key mod 13,现有18,用开放地址法
处理冲突。
解:其处理方法如下图所示。
181918 3370
0123456789101 12
…
15
二次探测线性探测
数
据
结
构
之
查
找
48
例:H(key)= key mod 13 , 采用线性探测再散列法处理冲突, m=16
对关键字19,14,23,01,68,20,84,27,55,11,10,79
19,14,23,01,68,20,84,27,55,11,10,79
61101 376 1 3 11 10 1
↓ ↓↓↓ ↓↓
2 7 2 4 11 2
↓↓ ↓ ↓↓
8 3 5123
4 4
↓
.
.
.
↓
9
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
19
14 2301 68 20 8427 55 11 1079
25
数
据
结
构
之
查
找
49
H(key)= key mod 13
对关键字19,14,23,01,68,20,84,27,55,11,10,79
链地址法链地址法处理的结果为:
0
1
2
3
4
5
6
7
8
9
10
11
12
79
01 14
6855
1984
20
2310
11
∧
∧
∧
∧
∧
∧
27
∧
∧
∧
∧
∧
∧
∧
数
据
结
构
之
查
找
50
0
1
2
3
4
5
6
7
8
9
10
11
12
∧
∧
68
∧
∧
19
20
∧
∧
23
11
∧
14
Hash表
01
27
55
10
79
84
溢出表
顺序查找
H(key)= key mod 13
对关键字19,14,23,
01,68,20,84,27,
55,11,10,79
公共溢出区法公共溢出区法处理的结
果为:
26
数
据
结
构
之
查
找
51
两种方法平均查找长度的比较:
链地址法:
ASL(12)=(1*6+2*4+3+4)/12=1.75
线性探测再散列:
ASL(12)=(1*6+2+3*3+4+9)/12=2.5
因此,线性探测在散列在处理冲突的过程中
易产生记录的二次聚集,而链地址法处理冲
突时不会发生类似情况,故其平均查找长度
较优。
数
据
结
构
之
查
找
52
?哈希表算法描述
1.计算哈希地址#define ListLen 13
int Hash(int key){return(key %Listlen);}
2.哈希表查找
int Search_H(int A[ ] , int key){
k=Hash(key);
for(i=0;i<ListLen ;i++){
j=(k+i)%ListLen;
if(A[ j]==0 || key==A[j] ) return j; }
return k;
}
27
数
据
结
构
之
查
找
53
3. 在哈希表中插入关键字key
Insert_Hash( int H[ ] ; int key ){
k=Search_H( H , key);
if( H[k]==0 ) H[k]=key ;
else if(key==H[k]) printf(“表中已存在该关键字”);
else printf(“哈希表已满!! ”);
}
main( ){ int H[ListLen] , i, key;
for( i=0; i<ListLen; i++) H[ i ]=0; //置空
for(i=0;i<ListLen-2 ; i++){
scanf(“%d”, &key);
Insert_Hash(H, key); }
}
数
据
结
构
之
查
找
54
要求掌握的内容
1. 熟练掌握除留余数法,会构造Hash函数。
2. 根据哈希函数和处理冲突的方法,
(1)手工构造已知序列的哈希表。
(2)编写构造哈希表的算法。
(3)编写在哈希表上的查找算法;
(4)在记录的查找概率相等的前提下,
计算哈希表平均查找长度。
28
数
据
结
构
之
查
找
55
?本章重点
?与查找有关的基本概念及存储结构。
?静态查找表:顺序表的查找,有序表的查
找,索引顺序表的查找。
?动态查找表:二叉排序树的构造过程,插
入及删除操作;平衡二叉树几种类型的调
整;B-树的定义,插入及删除操作。
?各种查找方法的性能比较。
?哈希函数及哈希表;哈希函数的构造方法;
处理冲突的方法。
第九章查找
作业
29
数
据
结
构
之
查
找
57
查找 ——作业
1. 已知一组关键字为(38,65,20,18,50,90,35,
80,100,95,61,19),依次插入关键字生成一
棵二叉排序树,问:
(1)查找关键字等于61的结点记录需比较次;
(2)与61比较的关键字序列是;
(3)如果将关键字为85的记录插入到树中,它将作
为结点的孩子结点;
(4)若要删除关键字为65的记录,可以取或
替换65作为38结点的右子树的根。
2. 什么叫二叉平衡树?试按下列次序将各关键字插入
到二叉平衡树中,画出重新平衡的情况。
(8,9,10,2,1,5,3,6,4,7,11,12)
数
据
结
构
之
查
找
58
编写算法
1. 在二叉排序树T(二叉链存储)中插入一个结点p;
2. 在二叉排序树T中删除任一结点q;
3. 根据给定关键字序列,生成一棵二叉排序树。
4. 分别对LL型、RR型、LR型、RL型的二叉排序
树T( T所指结点的平衡因子为2 或-2 )进行平衡
化处理。
5. 设计实现分块查找的算法。
30
数
据
结
构
之
查
找
59
哈希表作业
设哈希表长为13,散列函数为H(k)=k mod 13,
关键字序列为7,4,1,14,100,30,5,9,
20,134;试求:
(1)散列后的表中关键字分布(假定解决
冲突的方法为线性探测再散列法);求平均
查找长度ASL;
(2)求出散列后的表的装填因子;
(3)用链地址法解决冲突,给出相应的哈
希表,求ASL;
(4)分别计算该序列用顺序查找法和排序
之后用折半查找的ASL。