顺序查找和折半查找
顺序查找
一、算法思想
顺序查找,又叫“线性查找
”,通常用于线性表
。
算法思想:从头到尾
挨个找(或者反过来也OK)
二、算法实现
结构体定义
typedef struct{
ElemType *elem;
int TableLen;
}SSTable;
1.不使用哨兵
int seqSearch(SSTable ST, ElemType key){
for(int i = 0; i < ST.TableLen; i++){
if(ST.elem[i] == key){
return i;
}
}
return -1;
}
2.使用哨兵(0号位置存哨兵)
int seqSearchSentinel(SSTable ST, ElemType key){
elem[0] = key;
for(int i = ST.TableLen; i >= 0; i--){
if(elem[i] == key){
return i;
}
}
}
优点:无需判断是否越界,效率更高
3.查找效率分析
ASL成功=(1+2+3+…+n)/n=(n+1)/2
ASL失败=n+1
三、算法优化
1.顺序查找的优化(有序表)
用查找判定树分析ASL
共有n+1种查找失败的情况(n+1个失败结点,n个成功结点)
ASL失败=(1+2+3+…+n+n
)/(n+1)=n/2+n/(n+1)
一个成功结点的查找长度=自身所在层数
一个失败结点的查找长度=其父节点所在层数
默认情况下,各种失败情况或成功情况都等概率发生
2.顺序查找的优化(被查概率不相等)
被查概率大的放在靠前位置
折半查找
一、算法思想
1.概念
折半查找,又称“二分查找”,仅适用于有序
的顺序表
(顺序表拥有随机访问的特性,链表没有)。
2.查找过程:
初始:
low =0
high = TableLen - 1
mid = [(low + high) / 2]这里是向下取整
如果要查找的元素大于mid:
low = mid + 1
如果要查找的元素小于mid:
high = mid - 1
如果要查找的元素等于mid:
直接返回mid
如果low == high == 要查找的元素:
查找成功
如果low在high的右边:
查找失败
二、算法实现
1.定义
typedef struct{ //查找表的数据结构(顺序表)
ElemType *elem; //动态数组基址
int TableLen; //表的长度
}SSTable;
2.折半查找
基于升序
binarySearch1
基于降序
binarySearch2
三、查找判定树
- 如果当前low和high之间
有奇数个
元素,则mid分隔后,左右两部分元素个数相等
- 如果当前low和high之间
有偶数个
元素,则mid分隔后,左半部分比右半部分少一个元素
- 折半查找的判定树中,若mid = [(low + high) / 2]向下取整,则对于任何一个结点,必有
右子树结点数 - 左子树结点数 = 0或1
- 折半查找的判定树
一定是平衡二叉树
- 折半查找的判定树中,
只有最下面一层是不满的
- 因此,元素个数为n时
树高h=[log2(n+1)]向上取整
(计算方法同“完全二叉树”) - 判定树结点关键字:
左<中<右
,满足二叉排序树的定义 - 对于有n个成功结点的折半查找判定树,
失败结点:n+1个
(等于成功结点的空链域数量) 树高h=[log2(n+1)]向上取整
(该树高不包含失败结点)树高h+1=[log2(n+1)]向上取整
(该树高包含失败结点)- 查找成功的ASL<=h
- 查找失败的ASL<=h
- 折半查找的
时间复杂度 = O(log2n)
四、折半查找效率
ASL成功=(1 * 1 + 2 * 2 + 3 * 4 + 4 * 4) / 11 = 3
ASL失败=(3
* 4 + 4
* 8) / 12 = 11/3
五、拓展思考
1.折半查找的速度一定比顺序查找更快吗?
不一定,比如要查的元素就是第一个元素
2.如果mid=[(low+high)/2]向上取整,判定树时什么样子?
- 如果当前low和high之间
有奇数个
元素,则mid分隔后,左右两部分元素个数相等
- 如果当前low和high之间
有偶数个
元素,则mid分隔后,左半部分比右半部分多一个元素
- 折半查找的判定树中,若mid = [(low + high) / 2]向上取整,则对于任何一个结点,必有
左子树结点数 - 右子树结点数 = 0或1
分块查找
一、算法思想
特点:块内无序、块间有序
定义
//索引表
typedef struct{
ElemType maxValue;
int low, high;
}Index;
//顺序表存储实际元素
ElemType List[100];
分块查找
,又称索引顺序查找
,算法过程如下:
1.在索引表
中确定待查记录所属的分块(可顺序
、可折半
)
2.在块内顺序查找
用折半查找查索引
若索引表中不包含目标关键字,则折半查找索引表最终停在low>high
,要在low所指分块中查找
。
原因:最终low左边一定小于目标关键字,high右边一定大于目标关键字。而分块存储的索引表中保存的是各个分块的最大关键字。
low超出索引表范围,查找失败。
二、查找效率分析(ASL)
共有14个元素,各自被查概率为1/14
若索引表采用顺序查找
,则7:2次
、10:3次
、13:3次
…
若索引表采用折半查找
,则30:4次、27:2次?
ASL=∑PiCi
查找失败的情况更复杂…一般不考
假设,长度为n的查找表被均匀地分为b块,每块s个元素
(n=sb --> b=n/s)
设索引查找和块内查找的平均查找长度分别Li、Ls,则分块查找的平均查找长度为ASL=Li+Ls
1.用顺序查找查索引表
,(这个是重点)
则Li=(1+2+…+b)/b=(b+1)/2,Ls=(1+2+…+s)/s=(s+1)/2,
则ASL=(b+1)/2+(s+1)/2=(s^2+2s+n)/2s,
当s=(根号n)时
,ASL最小
= (根号n) + 1
若n=10000,则ASLmin=101
2.用折半查找查索引表
,
则Li=[log2(b+1)]向上取整,Ls=(1+2+…+s)/s=(s+1)/2
则ASL=[log2(b+1)]向上取整+(s+1)/2
三、拓展思考
若查找表是“动态查找表”,有木有更好的实现方式?
链式存储