///
/// 顺序搜索法(Sequential Search)
/// 优点: 该方法对数据无事先排序(Sorting)要求, 容易实现
/// 缺点: 搜索效率差(平均搜索 (N + 1) / 2 次), 不管是否排序, 每次都必须从头到尾遍历一次
/// 时间复杂度:
/// 如果没有重复数据,线性查找在找到任一元素时就终止,否则需要全部遍历. 在最差情況下, 需作N次比较, O(N)
/// 在平均狀況下(各元素出现与分布的概率相等)需 (N + 1) / 2次比较,所以最差时间为O(N), 最好时间为O(1) = 1次
///
template int sequential_search(const R *list, int n, const R &key)
{
for (int i = 0; i < n; ++i)
{
if (list[i] == key) return i;
}
// not found
return -1;
}
template int sequential_search_last(const R *list, int n, const R &key)
{
while (--n >= 0) {
if (list[n] == key) return n;
}
// not found
return -1;
}
//-------------------------------------------------------------------------
///
/// 二分搜索法(Binary Search)
/// 优点: 搜索效率优(平均搜索 Log2N 次)
/// 缺点: 数据必需事先排序且必需可直接随机存取
/// 时间复杂度:
/// 每次比较都会比上一次少一半数据, 2^x = N, x = Log2N, 因此平均时间 O(LogN)
///
template int binary_search(const R *list, int n, const R &key)
{
int _lower = 0;
int _upper = n - 1;
int _mean;
while (_lower <= _upper) {
_mean = (_lower + _upper) >> 1;
if (list[_mean] == key) {
return _mean;
}
if (list[_mean] > key) {
_upper = _mean - 1;
} else {
_lower = _mean + 1;
}
}
// not found
return -1;
}
template int binary_search_recursion(const R *list, int _lower, int _upper, const R &key)
{
assert(_lower <= _upper);
int _mean = (_lower + _upper) >> 1;
if (list[_mean] == key) {
return _mean;
}
if (_lower == _upper){
// not found
return -1;
} //if
if (list[_mean] > key) {
return binary_search_recursion(list, _lower, _mean - 1, key);
}
return binary_search_recursion(list, _mean + 1, _upper, key);
}
//-------------------------------------------------------------------------
///
/// 插值搜索法(Interpolation Search)
/// 定义: 二分搜索法的一种改进, 依照数据位置分布, 运用插值预测数据所在位置, 再以二分法方式逼近
/// 插值是离散函数逼近的重要方法, 利用它可通过函数在有限个点处的取值状况, 估算出函数在其他点处的近似值
/// 优点: 数据分布均匀时搜索速度极快
/// 缺点: 数据必需事先排序且需计算预测公式
/// 时间复杂度:
/// 取决于数据分布情形, 平均时间 O(LogLogN) 优于 二分搜索 O(LogN)
///
template int interpolation_search(const R *list, int n, const R &key)
{
int _lower = 0;
int _upper = n - 1;
int _mean;
while (_lower <= _upper)
{
// predictor
_mean = (_upper - _lower) * (key - list[_lower]) / (list[_upper] - list[_lower]) + _lower;
if (_mean < _lower || _mean > _upper) {
// mean misprediction
break;
}
if (list[_mean] == key) {
return _mean;
}
if (key < list[_mean]) {
_upper = _mean - 1;
} else {
_lower = _mean + 1;
}
}
// not found
return -1;
}
//-------------------------------------------------------------------------
///
/// 斐波那契搜索法(Fbonacci Search)
/// 定义: 利用斐波那契数列的性质(黄金分割原理)预测数据所在位置, 再以二分法方式逼近
/// 优点: 只涉及加法和减法运算
/// 缺点: 数据必需事先排序且需计算或者预定义斐波那契数列
/// 时间复杂度:
/// 同于二分搜索 O(LogN), 平均情况下, 斐波那契查找优于二分查找, 但最坏情况下则劣于二分查找
///
template int fbonacci_search(const R *list, int n, const R &key)
{
// 预定义斐波那契数列, 代码计算可参考http://blog.csdn.net/rrrfff/article/details/6848700
static int F[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 };
int _lower = 0;
int _upper = n - 1;
int _mean;
// 计算 n 在斐波那契数列中位置
int _index = 0;
while (n > F[_index] - 1)
++_index;
// 斐波那契搜索要求 n == F[_index] - 1
R *_list;
int _count = F[_index] - 1;
if (n != _count) {
assert(n < _count);
_list = new R[_count];
memcpy_s(_list, _count * sizeof(R), list, n * sizeof(R));
// 填补长度, _count则为填充后的长度
for (int i = n; i < _count; ++i)
_list[i] = _list[n - 1];
} else {
_list = const_cast(list);
} //if
while (_lower <= _upper) {
// 利用斐波那契数列确定下一个要比较的元素位置
_mean = _lower + F[_index - 1] - 1;
if (key == _list[_mean]) {
if (_list != list) delete[] _list;
if (_mean <= n - 1)
return _mean;
else
return n - 1;
} //if
if (key < _list[_mean]) {
_upper = _mean - 1; // 待查找的元素在[_lower, _mean -1]范围内
// 此时范围[_lower, _mean -1]内的元素个数为 F[_index - 1] - 1
// 所以
--_index;
} else {
_lower = _mean + 1; // 待查找的元素在[_mean + 1, _upper]范围内
// 此时范围[_lower, _upper]内的元素个数为 _count - F[_index - 1] =
// (F[_index] - 1) - F[_index - 1] = (F[_index] - F[_index - 1]) - 1 =
// F(_index - 2) - 1个(因为 F(n) = F(n - 1) + F(n - 2)(n≥2, n∈N*))
// 所以
_index -= 2;
}
}
if (_list != list) {
delete[] _list;
} //if
// not found
return -1;
}
//-------------------------------------------------------------------------
int _tmain(int argc, _TCHAR* argv[])
{
const int a[] = { 1, 3, 5, 18, 26, 39, 48, 66 };
for each (auto k in a)
{
int r1 = sequential_search(a, RLIB_COUNTOF(a), k);
int r2 = sequential_search_last(a, RLIB_COUNTOF(a), k);
int r3 = binary_search(a, RLIB_COUNTOF(a), k);
int r4 = binary_search_recursion(a, 0, RLIB_COUNTOF(a) - 1, k);
int r5 = interpolation_search(a, RLIB_COUNTOF(a), k);
int r6 = fbonacci_search(a, RLIB_COUNTOF(a), k);
printf(RLIB_NEWLINEA);
}
system("pause");
}
此外, 还有
二元树搜索法(Tree Search): 先将数据列建立为一棵二元搜索树, 树中每节点皆不小于左子树(叶), 也不大于右子树(叶), 即有左子树的值≦树根值≦右子树的值.该方法的优点是插入与删除时, 只需改变指标, 且二叉树搜索效率较高(介于线性查找与二分之间,平均与最差时间为O(N) ).而缺点是除了要求数据有序外还需较多的额外內存来存储左右节点)
哈希搜索法(Hashing Search): 存取数据时, 并不依数据顺序存取, 而是将数据按照某特定法则(Hash映射)转换为数据储存位置(slot), 以数据键值(key-value)进行转换.【优点】1.搜寻速度最快; 2. 数据不必先排序; 3.在没发生碰撞(collision)与溢出(overflow)情况下, 仅需一次即可读取; 4.搜寻速度与数据量大小无关; 5.具有一定保密性, 若不知哈希函数, 无法取得数据存储位置(slot). 【缺点】1.浪费空间(有溢位数据区, 解决冲突), 且储存空间的利用率比线性存储差; 2.有碰撞问题, 当冲突太多时会严重影响速度; 3.程序设计略复杂; 4.大数据情况下效率不佳; 5.不适合挂载媒介.
等查找搜索算法, 因为该类算法涉及的知识点较多, 本篇便不进行阐述, 有兴趣者可以自行查阅资料并给予实现.
参考文献
搜尋(Search). http://spaces.isu.edu.tw/upload/18833/3/web/search.htm