以下内容主要参考青岛大学王卓老师的数据结构与算法基础。https://www.bilibili.com/video/BV1nJ411V7bd?p=137&vd_source=f893624050c1893c80cd53fcdcf62cfe
查找的基本概念
在哪查? ---查找表
查找表是同一类型的数据元素构成的集合。
什么查找?
根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素或记录。
- 主关键字:唯一标识一个记录的关键字(比如准考证号)
- 次关键字:用于识别若干记录的关键字(比如姓名(存在同名))
查找成功否?
根据给定的某值,在查找表中确定其关键字等于给定值的数据元素或记录。若存在,则称“查找成功”,查找结果给出整个记录的信息或在查找表中的位置,否则称“查找失败”,结果给出空记录或空指针。
查找表的分类?
静态查找表:仅作“查询”操作的查找表。
动态查找表:作“插入”和“删除”操作的查找表。
线性表的查找
一、顺序查找(线性查找)
应用范围
- 顺序表或线性链表表示的静态查找表
- 表内元素之间无序
顺序表的表示
数据元素类型定义
typedef struct{
KeyType key; //关键字域
... //其他域
}ElemType;
typedef struct{ //顺序表结构类型定义
ElemType *R; //表基址
int len; //表长
}SSTable;
SSTable ST;
在顺序表ST中查找值为key的数据元素
eg:在ST中查找13,找到,返回“5”.查找60,未找到,返回“0”.
算法1实现
int Search_Seq(SSTable ST,KeyType key)
{
for(int i = ST.len; i >= 1; --i)
if(ST.R[i].key == key)
return i;
return 0;
}
算法2实现
int Search_Seq(SSTable ST,KeyType key)
{
for(int i = ST.len; ST.R[i].key != key; --i)
if(i <= 0) break;
if(i > 0 ) return i;
else return 0;
}
算法3实现
int Search_Seq(SSTable ST,KeyType key)
{
for(int i = ST.len; ST.R[i].key != key && i > 0; --i);
if( i > 0 )
return i;
else
return 0;
}
改进---监视哨
在待查关键字key存入表头(“哨兵”、“监视哨”),从后往前逐个比较,可免去查找过程中每一步都要进行检测是否查找完毕,加快速度。---不用考虑是否越界
eg:查找“60”,在表头存入“60”,称为“哨兵”。
算法4实现:
int Search_Seq(SSTable ST,KeyType key)
{
ST.R[0].key = key;
for(int i = ST.len; ST.R[i].key != key; --i);
return i;
}
当ST.len较大时,此改进能使进行一次查找所需的平均时间几乎减少一半。
时间效率分析
比较次数与key位置有关:
- 查找第i个元素,需要比较n-i+1次
- 查找失败,需比较n+1次
时间复杂度O(n):
查找成功时的平均查找长度,设表中个记录查找的概率相等
空间复杂度: 一个辅助空间------O(1)
二、折半查找法(二分或对半查找)
折半查找:每次将待查记录所在区间缩小一半。
查找算法:
设表长为n, low、high和mid分别指向待查元素所在区间的上界、下界和中点,key为给定的要查找的值:
初始时,令low=1, high=n, mid=向下取整[(low+hhigh)/2]
设k与mid指向的记录比较
若key==R[mid].key, 则查找成功
若key<R[mid].key, 则high=mid-1
若key>R[mid].key, 则low=mid+1
重复上述操作,直到low>high时,查找失败。
算法1实现:
int Search_Bin(SSTable ST,KeyType key)
{
low = 1; high = ST.len; //设置区间初值
while(low <= high){
mid = (low +high) / 2;
if(ST.R[mid].key == key)
return mid; //找到待查元素
else if(key < ST.R[mid].key) //缩小查找区间
high = mid - 1; //在前半段区间查找
else
low = mid + 1; //在后半段区间查找
}
return 0; //不存在此元素
}
算法2实现:(递归算法)
int Search_Bin(SSTable ST,KeyType key, int low ,int high)
{
if(low > high) return 0;
mid = (low +high) / 2;
if( key == ST.elem[mid].key)
return mid;
else if(key < ST.elem[mid].key)
return mid;
.....//递归,在前半段去检查找
else ... //递归,在后半段区间查找
return 0;
}
折半查找的性能分析---判定树
查找成功
- 比较次数 = 路径上的结点数
- 比较次数 = 结点的层数
- 比较次数 <= 树的深度 = 向下取整【log2 n】+ 1
查找不成功
比较次数 = 路径上的内部节点数
顺序查找为平均长度为6 , 二分法查找平均长度为3 .
优缺点:
- 优点:效率比顺序查找高
- 缺点:只适用有序表,且限于顺序存储结构(对线性链表无效)
三、分块查找(索引顺序查找)
查找过程:先确定待查记录所在的块(顺序或折半查找),再在块内查找(顺序查找)。
条件:
将表分成几块,且表有序或者分块有序(第二块的元素都大于第一块的最大值);
建立索引表
eg:假设要查找的元素为38
通过索引表,38在第二块上,在第二块进行顺序查找
分块查找性能分析
查找效率:
- Lb: 对索引表查找的ASL
- Lw:对块内查找的ASL
eg: 当n=9,s=3时,ASLbs = 3.5,而折半法为3.1,顺序法为5
优缺点
- 优点:插入和删除比较容易,无需大量的移动
- 缺点:要增加一个索引表的存储空间并对初始索引表进行排序运算。
- 适用情况:如果线性表既要快速查找又经常动态变化,则采用分块查找。
查找方法比较