05基于顺序、二分、桶、二叉查找树的检索算法
一、问题
在一个有序数组arr[1…n]中查找x,如果x在数组arr[]中,输出x的下标 j ;如果x不在arr[]中,输出 j = 0.(为避免数值含义重合,我们假定数组下标由1开始)
如何以行之有效的方法检索出目标值x呢?
二、几种检索算法
1.顺序检索
1.1解析
顺序检索是最容易想到、也是最简单的检索方式。
顺序检索的核心思想:
按顺序逐一判断数组中的数据是否符合要求,若符合,返回下标索引;反之,继续判断下一个。若遍历结束,没找到目标x,返回0.
1.2设计
//顺序检索
int linearSearch(int arr[], int n, int x) {
for ( i from 0 to n-1 )
if (arr[i] == x)
return i+1;//输出下标从1开始
return 0;
}
1.3分析
不难得出,顺序检索复杂度最好情形为linearSearch()=O(1),最坏情形为linearSearch()=O(n).
我们知道,顺序检索适用范围广,无论数组是否有序,无论数据类型是整型还是浮点型,它都可以胜任。
但是,当n很大的时候,顺序检索的效率明显很低,是否有更好的检索方式呢?
2.二分查找检索
2.1解析
二分查找也称折半查找,它不同于顺序检索挨家挨户地做人口普查,每次判断后,它会有选择地筛选掉剩余一半的数据。
二分查找的基本思想:
1)
将n个元素分成大致相等的两部分,在升序数组中取arr[n/2]与x做比较。(降序数组类似)
2)
如果x=arr[n/2],则找到x,返回下标索引,算法中止;
如果x<arr[n/2],则在数组arr[]的左半部分继续搜索x;
如果x>arr[n/2],则在数组arr[]的右半部搜索x.
3)
当左部分与右部分重合依旧没有找到x,函数返回0.
2.2设计
//二分查找
int binSearch(int arr[], int n,int x) {
int i,left=0,right=n-1,mid;
while (下标范围左部分left <= 右部分right) {
//取中位数与x作比较,根据判断结果执行相应操作
mid = (left + right) / 2;
//mid即为目标数x的索引,终止函数
//输出下标从1开始
if (arr[mid] == x) return mid+1;
//查找数过小,缩小左部范围
else if(arr[mid] < x) left = mid+1;
//查找数过大,缩小右部范围
else right = mid-1;
}
return 0;
}
2.3分析
二分查找最好情形复杂度为binSearch()=O(1),最坏情形复杂度为binSearch()=O(logn).
虽然,该算法对数据有约束性,它要求:数据必须采用顺序存储结构,并且按关键字大小有序排列。
相较顺序查找,二分查找很大程度地提高了查找效率,尤其是当n较大的时候。那么,是否还有复杂度更小的检索算法呢?
3.桶检索
3.1解析
桶检索的核心思想是:
创建一个辅助数组a[],循环遍历arr[],使得a[arr[i]]=i.通俗来讲,就是把从下标索引到数值的映射转换为从数值到下标索引的映射。如此一来,a[x]即为我们需要的索引值。
3.2设计
//桶检索
int sup_arr[10000]={0};//辅助数组
void transArr(int a[],int n){
for ( i from 0 to n-1 ) {
sup_arr[a[i]] = i+1;//输出下标从1开始
}
}
//桶检索
int bottleSearch(int x) {
return sup_arr[x]+1;
}
3.3分析
桶检索的本质是以空间换取时间,提高检索效率。查找前创建辅助数组,查找的复杂度可降至为O(1).
遗憾的是,该方法不针对有序数组,却有较强的数据约束性——它要求数据类型为整型、数值不能重复、且数值上限不能很大。对于百万级、千万级的数据,有限的空间资源会制约它的使用范围。
4.二叉查找树检索
二叉查找树:
一类二叉树,其每个节点都含有一个键以及对应的值。若满足每个节点的键都大于左子树中任意节点的键而小于右子树中任意节点的键,则称这类二叉树为二叉查找树。
二叉查找树的性质:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
4.1解析
基于二叉查找树的性质和数据的存储结构,它支持快速查找数据。
4.2设计
//关键值数据类型定义
typedef int T;
//节点定义
struct node {
T key;
int index;
node* left, * right;
};
//二叉查找树检索
T findBtree(node* root, int x) {
node* n = (node*)malloc(sizeof(node));
n = root;
while (n) {
if (n->key == x) return n->index + 1;//输出下标从1开始
else if (n->key < x) n = n->right;
else n = n->left;
}
return 0;
}
4.3分析
二叉查找树复杂度findBtree()=O(logn).
此法适用范围较广,速度较快,对数据类型要求不高。
三、源码
https://github.com/KabgRs/F-SDDR/tree/%E7%AE%97%E6%B3%95%E5%88%86%E6%9E%90%E4%B8%8E%E8%AE%BE%E8%AE%A1%E4%BD%9C%E4%B8%9A/05%E5%9B%9B%E7%A7%8D%E6%A3%80%E7%B4%A2%E7%AE%97%E6%B3%95_%E9%A1%BA%E5%BA%8F_%E4%BA%8C%E5%88%86_%E6%A1%B6_%E4%BA%8C%E5%8F%89%E6%9F%A5%E6%89%BE%E6%A0%91
(搜索栏粘贴链接时注意删掉原创声明)