/// <summary> /// 顺序搜索法(Sequential Search) /// 优点: 该方法对数据无事先排序(Sorting)要求, 容易实现 /// 缺点: 搜索效率差(平均搜索 (N + 1) / 2 次), 不管是否排序, 每次都必须从头到尾遍历一次 /// 时间复杂度: /// 如果没有重复数据,线性查找在找到任一元素时就终止,否则需要全部遍历. 在最差情況下, 需作N次比较, O(N) /// 在平均狀況下(各元素出现与分布的概率相等)需 (N + 1) / 2次比较,所以最差时间为O(N), 最好时间为O(1) = 1次 /// </summary> template<typename r = int> 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<typename r = int> 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; } //------------------------------------------------------------------------- /// <summary> /// 二分搜索法(Binary Search) /// 优点: 搜索效率优(平均搜索 Log2N 次) /// 缺点: 数据必需事先排序且必需可直接随机存取 /// 时间复杂度: /// 每次比较都会比上一次少一半数据, 2^x = N, x = Log2N, 因此平均时间 O(LogN) /// </summary> template<typename r = int> 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<typename r = int> 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); } //------------------------------------------------------------------------- /// <summary> /// 插值搜索法(Interpolation Search) /// 定义: 二分搜索法的一种改进, 依照数据位置分布, 运用插值预测数据所在位置, 再以二分法方式逼近 /// 插值是离散函数逼近的重要方法, 利用它可通过函数在有限个点处的取值状况, 估算出函数在其他点处的近似值 /// 优点: 数据分布均匀时搜索速度极快 /// 缺点: 数据必需事先排序且需计算预测公式 /// 时间复杂度: /// 取决于数据分布情形, 平均时间 O(LogLogN) 优于 二分搜索 O(LogN) /// </summary> template<typename r = int> 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; } //------------------------------------------------------------------------- /// <summary> /// 斐波那契搜索法(Fbonacci Search) /// 定义: 利用斐波那契数列的性质(黄金分割原理)预测数据所在位置, 再以二分法方式逼近 /// 优点: 只涉及加法和减法运算 /// 缺点: 数据必需事先排序且需计算或者预定义斐波那契数列 /// 时间复杂度: /// 同于二分搜索 O(LogN), 平均情况下, 斐波那契查找优于二分查找, 但最坏情况下则劣于二分查找 /// </summary> template<typename r = int> 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<r>(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