各类经典搜索算法(Search Algorithms)的简单介绍和C++实现

/// <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

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值