查找的基本概念
查找 根据某个给定的值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)
查找表(Search Table) | 由同一类型的数据结构(或记录构成的集合) |
---|---|
关键字(Key) | 数据结构中某个数据项的值(键值) |
主关键字 | 唯一地标记一个记录 |
次关键字 | 用以识变多个数据元素(记录)的关键字 |
操作方式 | |
---|---|
静态查找表(Static Search Table) | 只作查找操作的查找表 |
动态查找表(Dynamic Search Table) | 在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素 |
该篇文章主要展示线性表的查找,操作方式为静态查找
顺序表结构定义
#include<stdio.h>
#define OK 1
#define ERROR 0
#define MAXSIZE 100 //数组存储空间最大长度
typedef int Status; //函数返回类型
测试数据
int a[MAXSIZE] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18};
顺序查找
概念:顺序查找是按照序列原有顺序对数组进行遍历比较查询的基本查找算法。
基本原理 : 对于任意一个序列以及一个给定的元素,将给定元素与序列中元素依次比较,直到找出与给定关键字相同的元素,或者将序列中的元素与其都比较完为止。
顺序查找(无哨兵)
/* 顺序查找(无哨兵)返回位置,a为数组,n为查找的数组元素个数,key为要查找的关键字 */
// n != MAXSIZE
//如果查找不成功,要用到a[n]的值,a[MAXSIZE]的值不确定
int Sequential_Search(int* a, int n, int key)
{
for (int i = 0; i <= n; i++)
{
if (a[i] == key)
{
return i;
}
}
//printf("未找到\n");
return ERROR;
}
有哨兵顺序查找
无哨兵的顺序查找每次循环需要判断两次(i<=n和a[i]==key),这里可以优化成一次。
同时将数组首元素存入key(循环从尾部开始且数组首元素地址不存数据元素),循环到数组首元素时,即未查找到。
/* 有哨兵顺序查找*/
// n != MAXSIZE
//如果查找不成功,要用到a[n]的值,a[MAXSIZE]的值不确定
Status Sequential_Search2(int* a, int n, int key)
{
a[0] = key; //a[0] 为哨兵
int i = n; //循环从尾部开始
while (a[i] != key)
{
i--;
}
return i; //返回0即未找到
}
有序表的查找
折半(二分)查找
算法要求:1.必须采用顺序存储结构。 2.必须按关键字大小有序排列。
查找过程:首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
/* 折半查找 */
Status 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; //即查找成功
}
//printf("未找到\n");
return ERROR;
}
插值查找
思路:
在查找中:不一定每次仅取 1/2 折半查找,插值查找就适用于关键字分布均匀的查找表
方法:
在折半查找中只需将折半步骤换成:
mid = low + (high - low) * (key - a[low]) / (a[high] - a[low]);
注:在折半查找中
high = MAXSIZE 容易出意外(即n != MAXSIZE)
因为要用到a[high]的值,若 high = MAXSIZE,该值 a[MAXSIZE]不确定
斐波那契查找(黄金分割法)
概念:斐波那契搜索就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为Fn,完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。
斐波那契查找的疑难解释
斐波那契数列:1、1、2、3、5、8、13、21、34…
若从34开始:如果要查找的元素在前半段,那么继续按照斐波那契数列来看,34 = 21 + 13,所以继续把前半段分成前21个数据元素的前半段和后13个元素的后半段,继续查找,如此反复,直到查找OK或ERROR。
需着重理解
斐波那契查找与其它查找都是分隔点的不同而已。斐波那契查找仅仅改变了中间结点(mid)的位置,mid不再是中间或插值得到,而是位于黄金分割点附近。
左右分区不包括 mid ,所以左右分区要各减去这个黄金分割点的长度
斐波那契额数列公式:F[k] = F[k - 1] + F[k - 2];
变式为:(F[k] - 1) =( F[k - 1] - 1)+ (F[k - 2] - 1)+ 1 ;
变式的最后这个 1 即 mid 点的长度
代码中的 k – 与 k -= 2 语句
k-- 是因为查找值小于当前分隔值,它的长度是 F[k-1] - 1,之后 F[k - 1] - 1又作为一个整体进行查找,则k需要自减。
k -= 2 是因为查找值大于当前分隔值,它的长度为 F[k - 2] - 1,之后 F[k - 2] - 1也会作为一个整体来查找,所以k要减2。
斐波那契查找
/* 斐波那契查找 */
// n != MAXSIZE
//后续将不满的数值补全时,
//需用到a[n](a[n]应为一个在查找当中的的值,一个确定的值),若 n = MAXSIZE,该值不确定,不符要求
Status Fibonacci_Search(int* a, int n, int key)
{
int F[100]; //斐波那契数列
F[0] = 0;
F[1] = 1;
for (int i = 2; i < 100; i++)
{
F[i] = F[i - 1] + F[i - 2];
}
int low, high, mid, k = 0;
low = 1; //最低下标
high = n; //最高下标
while (n > F[k] - 1) //计算n位斐波那契数列的位置
k++;
for (int i = n; i < F[k] - 1; i++) //将不满的数值补齐
{
a[i] = a[n];
}
while (low <= high)
{
mid = low + F[k - 1] - 1; //计算当前分隔的下标
if (key < a[mid]) //查找值小于当前分隔值
{
high = mid - 1;
k--;
}
else if (key > a[mid]) //查找值大于当前分隔值
{
low = mid + 1;
k -= 2;
}
else
{
if (mid <= n)
return mid;
else
return n; //mid > n说明是补全数值,返回n
}
}
return 0;
}
大家支持一下(^ ▽ ^)
树表查找 - 二叉排序树(C语言)