查找基本概念
查找表:由同一类型的数据元素(或记录)构成的集合。
关键字(键值):是数据元素中某个数据项的值
主关键字:该关键字可以唯一表示一个记录
次关键字:可以识别多个数据元素(或记录)
查找:根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)
查找按照操作方式可分:静态查找表和动态查找表
静态查找表:只做查找操作的查找表
1.查询某个特定的数据元素是否在查找表中
2.检索某个特定的数据元素和各种属性
动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已存在的某个元素数据
1.查找时插入数据元素
2.查找时删除数据元素
查找的效率是基于数据结构的,除了查找方式外,我们可以改变数据的存储结构来获得高效的查找。
静态查找适合线性表的数据结构;动态查找不妨使用二叉排序树;还有一些散列的查找技术
顺序表查找
顺序查找 (Sequential Search) 又叫线性查找,是最基本的查找技术, 它的查找过程是:从表中第一个(或最后一个)记录开始 , 逐个进行记录的关键字和给定值比较,若某个记录的关键字和给定值相等,则查找成功,找到所查的记录;如果直到最后一个(或第一个)记录,其关键字和给定值比较都不等时,则表中没有所查的记录,查找不成功 。
顺序查找算法
//a为数组 n为查找的数据个数 key为查找的关键字
int Sequential_Search(int *a;int n;int key)
{
int i;
for(i=1; i<n; i++)
{
if(a[i] == key)
return i;
}
return 0;
}
顺序查找优化(设置哨兵)
//a为数组 n为查找的数据个数 key为查找的关键字
int Sequential_Search2(int *a;int n;int key)
{
int i;
a[0] = key; //设置关键字值a[0]
i = n;
while(a[i]!=key)
{
i--;
}
return i;
}
两个算法的时间复杂度均为O(n),只是后一个在运算上免去了一次判断。
有序表查找
当数据进行有序排列之后,一个线性表有序时,对查找可以提高效率
折半查找(二分查找)
折半查找 (Binary Search) 技术,又称为二分查找。前提是线性表中的记录必须是关键码有序(通常从小到大有序) ,线性表必须采用顺序存储。折半查找的基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找,若给定值大于中间记录的关键字,则在中间记录的右半区继续查找。不断重复上述过程,直到查找成功,或所有查找区域无记录,查找失败为止。
二分查找算法
//二分查找
int Binary_Search(int *a,int n,int key)
{
int low,high,mid;
low = 1;
high = n; //定义数据记录的首位和末位
while(low <= high)
{
mid = (low+high)/2; //二分
if(key<a[mid]) //比较中值与查找值的大小
high = mid-1;
else if(key>a[mid])
low = mid+1;
else
return mid; //相等即说明找到返回mid
}
return 0;
}
时间复杂度为O(logn)
插值查找
插值查找(In田polation Search)是根据要查找的关键字 key 与查找表中最大最小记录的关键字比较后的查找方法,其核心就在于插值的计算公式更改 mid = (low+high)/2这行代码改为
虽然时间复杂度均为O(logn),对于分布平均的数据而言,插值查找方法要更好,对于分布极端不平均的情况,插值反而不是合适的选择
斐波那契查找
利用黄金分割原理实现,定义一个斐波拉契数组F,如下。
查找代码(核心思想是利用F数列来进行下标的分割)
int Fibonacci_Search(int *a,int n,int key)
{
int low,high,mid,i,k;
low = 1;
high = n; //记录最低和最高下标
k = 0;
while(n>F[k]-1) //计算n在斐波拉契数列中的位置记为k
k++;
for(i=n;i<F[k]-1;i++) //将不满的数值补全,补为最后的数字,使查找数组的长度等于F[k]-2
a[i] = a[n];
while(low<high)
{
mid = low + F[k-1] - 1; //计算分割的下标
if(key < a[mid]) //若查找值小于当前的分割记录
{
high = mid - 1; //最大下标调整至mid-1处
k = k-1; //k下标-1
}
else if(key > a[mid]) //若查找值大于当前分割记录
{
low = mid + 1; //最小下标调整至mid+1处
k = k - 2; //k下标—2
}
else
{
if(mid<=n)
return mid; //若相等,则说明mid即为查找到的位置
else
return n; //若为补全数值,则返回n
}
}
return 0
}
时间复杂度为O(logn),就平均性能而言,高于二分查找。
注意:
二分查找利用的是加法与除法
插值查找利用的是四则运算
斐波拉契查找只是利用简单的加减法
线性索引查找
数据量庞大时,多数按照先后顺序存储,快速查找需要引入索引。索引就是把一个关键字与它的对应的记录相关联的过程。
索引按照结构可以分为线性索引、树形索引、多级索引。
线性索引:将索引项集合组织为线性结构,也称索引表
方法分类:稠密索引、分块索引、倒排索引
稠密索引
稠密索引是指在线性索引中,将数据集中的每个记录对应一个索引项,如下,对于稠密索引这个索引表来说,索引项一定是按照关键码有序的排列。即可以用到可以用到折半、插值、斐被那契等有序查找算法。但是数据集庞大时,意味着索引也得同样的数据集长度规模,效率反而降低。
分块索引
分块有序,是把数据集的记录分成了若干块,并且这些块需要满足两个条件:
1.块内无序
2.块间有序
定义的分块索引的索引项结构分三个数据项 :
1.最大关键码,它存储每一块中的最大关键字,这样的好处就是可以使得在它之后的下一块中的最小关键字也能比这一块最大的关键字要大;
2.存储了块中的记录个数,以便于循环时使用;
3.用于指向块首数据元素的指针,便于开始对这一块中记录进行遍历。
查找步骤:
1.在分块索引表中查找要查关键字所在的块。
2.根据块首指针找到相应的块,并在块中顺序查找关键码。因为块中可以是无序的,因此只能顺序查找。
分块索引(n个记录分为m块,每块t条记录)查找的平均长度为
当m=t相等时,这种方法达到最优效果
倒排索引
索引项的通用结构是:次关键码和记录号表。其中记录号表存储具有相同次关键字的所有记录的记录号 (可以是指向记录的指针或者是该记录的主关键字) 。 这样的索引方法就是倒排索引。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引。
举例来说,根据输入的次关键码book来查找有序的单词表,找到对应的文章编号(可能有多个),即返回记录号表。