数据结构学习笔记-查找

主要内容

7.1 查找的基本概念
7.2 线性表的查找
7.3 树表的查找
7.4 散列表的查找

7.1 查找的基本概念

一、基本概念

1)查找表:同一类型的数据元素构成的集合;包括线性表、树表、散列表
2)关键字:是数据元素中某个数据项的值,用以标识数据元素(或记录),分主关键字和次关键字
3)查找(成功与不成功)
① 查询某个“特定的”数据元素是否在查找表中
② 检索某个“特定的”数据元素的各种属性
③ 在查找表中插入一个数据元素
④ 从查找表中删去某个数据元素
4)静态查找:仅作查询和检索操作,不改动查找表的数据
5)动态查找:查找表是在查找过程中动态生成的。在静态查找过程中,伴随着插入不存在的元素,删除已存在的元素
6)平均查找长度:对于含有n个记录的查找表,查找成功的平均查找长度为
在这里插入图片描述
Pi为查找第i个元素的概率
Ci为查找第i个元素时的关键字比较次数

二、线性表的顺序存储结构

——数据元素的表示

typedef struct 
{ 
	keyType key; 
	InfoType otherinfo; 
} ElemType ;

——顺序表的表示

typedef struct 
{ 
	ElemType *R; 
	int length; 
} SSTable;

7.2 线性表的查找

一、顺序查找

[算法思想]

1)可用顺序或链式存储结构
2)从后往前,依次比较。查找成功,返回位序,否则,返回0 (不存在的位序)
3)设置“哨兵”,避免位序控制

[算法描述]

 int Search_Seq(SSTable ST, KeyType key) 
 { 
 	for (i=ST.length;i>=1;--i) 
 		if (ST.R[i].key==key) 
 			return i; 
	 return 0; 
 } // Search_Seq

[改进算法]

int Search_Seq(SSTable ST, KeyType key) 
{ 
	ST.R[0].key=key; 
	for (i=ST.length; ST.R[i].key!=key;--i); 
	return i; 
} // Search_Seq 
//设置哨兵,避免每次比较i>=1,当n较大时,时间可减少一半

[性能分析]

在等概率查找的情况下,顺序表查找的平均查找长度为:
在这里插入图片描述
时间复杂度:T(n)=O(n)

二、折半查找

[基本要求]

1)采用顺序存储结构存放记录
2)表中记录按关键字有序

[算法思想]

1)将给定关键字key和处于中间位置的记录关键字进行比较, 若相等则查找成功
2)若不相等,则利用中间位置将查找表分为两个子表:若key 小,在前一子表继续查找;否则,在后一子表查找
3)重复(1)、(2),将查找区间不断对分,直到查找成功或失败

举例说明:查找key=64的记录
数据结构学习笔记-查找_第1张图片
low 指示查找区间的下界
high 指示查找区间的上界
mid=(low+high)/2 (向下取整)

[算法描述]

int Search_Bin ( SSTable ST, KeyType key ) 
{ 
	low=1; 
	high=ST.length;
	while (low<=high) 
	{ 
	mid=(low+high)/2; 
	if (key==ST.R[mid].key) 
		return mid; 
	else if ( key<ST.R[mid].key) 
			high=mid-1; 
		else 
			low=mid+1; 
	} //while 
	return 0; 
} // Search_Bin

[性能分析]

判定树
在这里插入图片描述
数据结构学习笔记-查找_第2张图片

1)查找关键字顶多走一条从根到叶的路径
2)待查找关键字有n个,则判定树高度h为└log2n┘+1
3)查找失败时,顶多进行h次关键字的比较
4)假设 n=2h-1 并且查找概率相等,则 :
在这里插入图片描述
该例题的ASL=(11+22+34+44)/11

三、索引顺序查找
也称分块查找,是顺序查找的一种改进

[基本思想]

1)为查找表建立索引表:关键字+指针
2)索引表按关键字有序,查找表按关键字有序或分块有序
3)查找过程:
①由索引表确定待查记录所在分块:折半查找,或顺序查找
②在对应的分块中查找记录:折半查找,或顺序查找
数据结构学习笔记-查找_第3张图片
1)查找表长度为n,分为b块,每块包含s=n/b个记录
2)每块查找概率为1/b,每个记录的查找概率为1/s
3)平均查找长度ASL=Lb+Ls
在这里插入图片描述
在这里插入图片描述
7.3 树表的查找

中序遍历:关键字递增序列!
数据结构学习笔记-查找_第4张图片
二叉排序树的二叉链表存储表示

typedef stuct 
{ 
	KeyType key; 
	InfoType otherinfo; 
}ElemType;
typedef stuct BSTNode 
{ 
	ElemType data; 
	stuct BSTNode *lchild,*rchild; 
}BSTNode,*BSTree;

2.查找

二叉排序树又称二叉查找树,其查找过程是一个从根结点开始,沿某一个分支逐层向下进行比较判等的过程

[算法思想]

1)从根结点开始,如果根指针为NULL,则查找不成功
2)否则用给定值key与根结点的关键字值T->data.key进行比较
①如果key== T->data.key,则查找成功返回根结点地址
②如果keydata.key,则递归查找根结点的左子树
③如果key>T->data.key,则递归查找根结点的右子树
数据结构学习笔记-查找_第5张图片
[算法描述]

BSTree SearchBST(BSTree T,KeyType key) 
{ 
	BSTree p=T; 
	if ((!T)||key==T->data.key) 
		return T; 
	else 
		if (key<T->data.key) 
			return SearchBST(T->lchild,key); 
		else 
			return SearchBST(T->rchild,key); 
} //SearchBST 
//T(n)=O(logn)

3.插入

[算法思想]

1)若BST为空,则待插入结点S作为根结点插入空树
2)若BST非空,则将key与T->data.key比较:
①若key==T->data.key:停止插入
②若keydata.key:将
S插入左子树
③若key>T->data.key:将*S插入右子树
3)新插入的结点一定是不成功结点的左孩子或右孩子

[算法描述]

void InsertBST(BSTree &T,ElemType e) 
{ 
	if (!T) 
	{ 
		s=new BSTNode; 
		s->data=e; 
		s->lchild=s->rchild=NULL; 
		T=S; 
	} else 
		if (e.key<T->data.key) 
			InsertBST(T->lchild,e); 
		else 
			InsertBST(T->rchild,e); 
}//InsertBST 
//T(n)=O(logn)

插入值为40的结点,和值为60的结点
数据结构学习笔记-查找_第6张图片
数据结构学习笔记-查找_第7张图片
小结
1)构造BST的过程为对无序序列进行排序的过程
2)利用BST插入结点不必移动已排好序的结点,只需添加一个 叶结点即可
3)初始序列不同,BST的形态也不同,查找性能ASL也不同, 最短为 Log2n,最长可能为n
4)BST既有类似于折半查找的特性,又可采用链表作存储结构 ,因此是动态查找表的适宜表示
数据结构学习笔记-查找_第8张图片
5. 删除

1)在BST中删除一个结点时,必须将断开的二叉链表重新链接 起来,并确保BST的性质不会丢失
2)为保证在执行删除后,其搜索性能不至于降低,还需要防止 重新链接后树的高度增加
3)假设BST中被删结点是p (PL和PR分别表示其左子树和右子 树),其双亲结点是f,并设p是f的左孩子。分三种情况:

① 若*p结点为叶结点,需修改双亲左指针

q=p; 
f->lchild=NULL; 
free(q);

数据结构学习笔记-查找_第9张图片
② 若*p结点只有左子树PL或只有右子树PR,只需令PL或PR直接成为 *f的左子树即可
数据结构学习笔记-查找_第10张图片
中序序列: PL P F
中序序列: P PR F
删后中序序列: PL/R F

③ 若*p结点左PL、右PR子树都存在
数据结构学习笔记-查找_第11张图片
中序序列:…CL C…(QL Q) SL S P PR F…

二、平衡二叉树—AVL树

1.定义

一棵AVL树或者是空树或者是具有下列性质的二叉树:
它的左子树和右子树都是AVL树,且左子树和右子树的高度之差的绝对值不超过1
结点的平衡因子(BF)定义为该结点的左子树的深度减去右子树的深度:
所有结点的BF只能是-1、0、1
若有结点BF的绝对值大于1,则非AVL树
数据结构学习笔记-查找_第12张图片
平衡二叉树的性质:
1、它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树;
2、常用算法有红黑树、AVL、Treap、伸展树等。在平衡二叉搜索树中,其高度一般都良好地维持在O(log(n)),大大降低了操作的时间复杂度;
3、若根结点层次为1,则高度为h的平衡二叉树最少有F(h + 2) -1个结点,其中F 为Fibonacci序列1, 1, 2, 3, 5, 8, 13, 21,…;
4、最小二叉平衡树的节点总数的公式如下 F(n)=F(n-1)+F(n-2)+1,可以参考Fibonacci(斐波那契)数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。

2.结论
1)使二叉树经过处理成为平衡二叉树(AVL)的过程称作平衡化
2)对二叉排序树(BST)进行平衡化得到平衡二叉排序树(BBST)
3)平衡二叉排序树(BBST)具有最佳ASL

3.创建
1)建立平衡二叉排序树的算法从一棵空树开始,通过输入一系列的关键字,逐步建立AVL树
2)在插入新结点时进行平衡旋转,分四种情形:
① LL型→单向右旋
② LR型→先左后右
③ RR型→单向左旋
④ RL型→先右后左
3)基准点:距离叶结点最近的不平衡结点
旋转点:从基准点出发,按照平衡化处理方法寻找旋转点

举例:以序列 { 16, 3, 7, 11, 9, 26, 18, 14, 15 }创建BBST
数据结构学习笔记-查找_第13张图片
数据结构学习笔记-查找_第14张图片
数据结构学习笔记-查找_第15张图片
三、B_树

1.定义 m 阶的B_树或为空,或满足:
1)每个结点至多有m棵子树
2)若根结点不是叶结点,则至少有两棵子树
3)除根结点之外的所有非终端结点至少有m/2棵子树
4)非终端结点包含以下信息: (n,A0,K1,A1,K2,A2,K3,A3,…,Kn,An) Ki (m/2-1≤i≤n)为关键字,且K1< K2 < … < Kn; Ai 为指向子树的指针(0≤i≤n), 且 Ai-1 所指子树上所有关键字均小于Ki, Ai 所指子树上所有关键字均大于Ki。
5)树中所有叶子结点出现在同一层次上且不带信息:指针为 空,看作失败结点

2.查找

两个过程交替进行:
1)沿指针查找结点
2)在结点的关键字中进行查找
查找结果处理:
1)若查找成功,则返回指向被查关键字所在结点的指针和关 键字在结点中的位置
2)若查找不成功,则返回插入位置
数据结构学习笔记-查找_第16张图片
四、B+树

1.定义
1)树中每个非叶结点最多有 m 棵子树,至少有m/2棵子树
2)根结点至少有 2 棵子树
3)所有的叶结点都处于同一层次上,包含了全部关键字及指 向相应数据元素的指针
4)叶结点本身按关键字从小到大顺序排列

2.m阶的B+树和B_树的差异
1)有n棵子树的结点中含有n个关键字
2)所有的叶子结点中包含了全部关键字的信息
3)所有的非终端结点可以看成是索引部分,结点中仅含有其子树(根结点)中最大(或最小)关键字

3.查找
(1)可从根结点索引查找
(2)也可从头指针顺序查找
数据结构学习笔记-查找_第17张图片
7.4 散列表的查找

一、什么是哈希表

1.定义 一般情况下,记录和存储位置不存在对应关系,查找依靠关键字值的比较:
1)顺序查找:等于、不等于
2)折半查找、二叉排序树查找:等于、小于、大于
查找的效率取决于和给定值进行比较的关键字个数

为避免关键字的比较有一个办法:预先知道所查关键字在表中的位置。
即:Loc=f(key)
对应的概念:
哈希(hash)—杂凑; 哈希函数; 哈希表;
哈希查找; 哈希造表/散列; 哈希地址
举例:{Zhao, Qian, Sun, Li, Wu, Chen, Han, Ye, Dai}
哈希函数 f(key) = (Ord(第一个字母) -Ord(‘A’)+1)/2
在这里插入图片描述
二、构造哈希函数要考虑的因素:
1)散列表的长度
2)关键字的长度
3)关键字的分布情况
4)计算散列函数的时间
5)记录的查找频率

应遵循的原则:
1)哈希函数是压缩映象,不同关键字可能对应同一地址,称之为冲突
2)哈希函数的构造方法很灵活,尽量构造一个“均匀的”函数,使冲突尽可能少地产生
3)必须构造配套的冲突处理方法

二、构造哈希函数

1)直接定址法
2)数字分析法
3)平方取中法
4)折叠法
5)除留余数法
6)随机数法
若是非数字关键字,则需先对其进行数字化处理

1.直接定址法
哈希函数为关键字的线性函数(这种哈希函数叫做自身函数) H(key)=key 或者 H(key)=a×key+b (其中 a和b为常数)

[举例-1]
有一个从1岁到100岁的人口统计表,年龄作为关键字, 哈希函数取关键字自身 H(key)=key
在这里插入图片描述
[举例-2]
有一个解放后出生的人口统计表,关键字是年份,哈希函数取关键字加一常数: H(key)=key+(-1949)+1
在这里插入图片描述
2.数字分析法
在预先知道所有关键字的前提下,选取关键字中分布均匀的若干位直接作哈希地址,或迭加后的值作哈希地址
注:此方法仅适合于能预先估计出全体关键字的每一位上各种数字出现的频度
数据结构学习笔记-查找_第18张图片
由于中间四位可当成是近于随机的,因此可取其中任意两位,或取其中两位与另外两位叠加求和后去掉进位作为哈希地址

3.平方取中法
1)以关键字的平方值的中间几位作为存储地址
2)适用于不知全部关键字或取值集中在局部的情形
注:
1)求“关键字的平方值” 的目的是“扩大差别”
2)平方值的中间各位又与整个关键字中各位相关
数据结构学习笔记-查找_第19张图片
4.折叠法
将关键字等分为几部分,取这几部分的迭加和作哈希地址,
有:移位迭加和间界迭加
注:适用于关键字位数很多且每一位上数字分布大致均匀的情况
数据结构学习笔记-查找_第20张图片
5.除留余数法
取关键字被某个不大于表长m的整数p除后所得余数作为哈希地址
设哈希函数为: H(key) = key%p
p应为不大于m 的质数或是不含20 以下的质因子的合数
注:该方法简单、实用
在这里插入图片描述
可见,由于 p 中含质因子 3, 则所有含质因子 3 的关键字均映射到 “3 的倍数”的地址上,从而增加了“冲突”的可能

6.随机数法
取关键字的随机函数值为其哈希地址
设哈希函数为: H(key) = Random(key)
其中,Random 为随机函数
注:适用于关键字长度不等时构造哈希函数

三、处理冲突的方法

1)“冲突”是指由关键字得到的哈希地址上已存有记录,则“处理冲突”就是为该关键字的记录找到另一个“空”的哈希地址
2)在处理冲突的过程中,可能再次发生冲突,由此得到一系列哈希地址H1,H2…Hn

1.开放定址法
在这里插入图片描述
其中:
H(key)为哈希函数
m为表长 Hi为哈希地址序列
di为增量序列,有三种取法对应三种冲突处理方法

三种冲突处理方式:

1)线性探测再散列
di=1,2,…,m-1
注:只要哈希表未满,总能找到一个不冲突的哈希地址

2)二次探测再散列 di=12,-12,22,-22,…,-k2
注:只有在m为形如4j+3的素数时可行。

3)随机探测再散列
di=伪随机数序列

[举例-1] 关键字序列为 {9,1,23,14,55,20,84,27}
哈希函数:H(key)=key%7,表长为10

1)采用线性探测法处理冲突,试画出对应的哈希表,并计算查 找成功和查找失败的ASL
2)采用二次探测法处理冲突,试画出对应的哈希表,并计算查 找成功的ASL

关键字序列: {9,1,23,14,55,20,84,27}
哈希函数:H(key)=key%7,表长为10

(1)线性探测法

在这里插入图片描述
ASLsucc=(14+22+31+51)/8
ASLunsucc=(6+5+4+3+2+1+4)/7

关键字序列: {9,1,23,14,55,20,84,27}
哈希函数:H(key)=key%7,表长为10

(2)二次探测法

在这里插入图片描述

ASLsucc=(14+22+3*2)/8=14/8

两个概念:
1)若干关键字的哈希地址相同称为冲突,这些关键字互称同义词。即key1≠key2但f(key1)=f(key2),则key1、key2互为“同义词”。比如55、20、27互为同义词
2)处理同义词冲突的过程中,发生的非同义词的冲突,称为“ 二次聚集”。 比如 84和1的二次聚集

2.链地址法:将所有哈希地址相同的记录都链接在同一链表中

[举例-2] 关键字序列为 {9,1,23,14,55,20,84,27}
哈希函数:H(key)=key%7
采用链地址法处理冲突,画出哈希表, 并计算查找成功和不成功的ASL
数据结构学习笔记-查找_第21张图片
ASLsucc=(14+23+3*1)/8
ASLunsucc=(3+2+3+1+1+1+4)/7

四、哈希表的查找

1)给定K值,由哈希函数求得哈希地址。若该位置为空,则查 找失败
2)若该位置不为空,且关键字值与给定值K相等,则查找成功
3)若关键字值与给定值 K 不相等,则根据冲突处理函数找“下 一个”位置,直至——为空,或相等 从查找过程得知,哈希表查找的平均查找长度ASL实际上并不等于零

决定哈希表查找ASL的因素:
1)哈希函数
2)冲突处理方法
3)饱和程度:装载因子

结论:哈希表的平均查找长度是α的函数,而不是 n 的函数
在这里插入图片描述
五、哈希表的删除
从哈希表中删除记录 时要作特殊处理,相应地需要修改查找的算法
数据结构学习笔记-查找_第22张图片

你可能感兴趣的:(数据结构)