折半查找又称为二分查找,插值查找是在折半查找算法基础上优化而来,斐波那契查找算法又是针对插值查找进一步优化的。
备注:这三种查找的前提是有序的。
二分查找(Binary Search):
二分查找也称折半查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
算法要求:
1.必须采用顺序存储结构 2.必须按关键字大小有序排列。
时间复杂度为: O(log n)
空间复杂度: O(1)
/// <summary>
/// 二分查找
/// </summary>
/// <param name="nums"></param>
/// <param name="key">要查找的对象</param>
/// <returns></returns>
public int BinarySearch(int[] nums, int key)
{
int low = 0, high = nums.Length - 1, middle;
while (low <= high)
{
middle = (low + high) / 2;
if (key > nums[middle])
{
low = middle + 1;
}
else if (key < nums[middle])
{
high = middle - 1;
}
else
{
return middle;
}
}
return -1;
}
插值查找(Interpolation Search):
针对均匀分布的数组,可以使用插值计算,针对mid进行优化。具体代码就是替换上述mid计算,代码如下:
mid = low + (key - nums[low]) * (high - low) / (nums[high] - nums[low]);
核心就在于查找的计算公式:
(key - nums[low]) / (nums[high] - nums[low])
应该说,从时间复杂度来看,它也是O(log n),但对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好得多。
备注:针对不均匀分布的数据,建议使用二分查找。
斐波那契查找:
斐波那契查找,它是利用黄金分割原理来实现的。本质是利用斐波那契数列来进行分割。
时间复杂度为:O(log n)
/// <summary>
/// 创建一个斐波那契数列
/// </summary>
public void CalculateFibonacci()
{
_fibonacci[0] = 0;
_fibonacci[1] = 1;
for (int i = 2; i < _fibonacci.Length; i++)
_fibonacci[i] = _fibonacci[i - 1] + _fibonacci[i - 2];
}
/// <summary>
/// 斐波那契查找
/// </summary>
/// <param name="nums"></param>
/// <param name="key">要查找的对象</param>
/// <returns></returns>
public int FibonacciSearch(int[] nums, int key)
{
int n = nums.Length - 1; // 数组最大有效下标
int low = 0, high = n, mid, k = 0;
int[] newNums;
CalculateFibonacci();// 计算斐波那契数列
// 找出数组长度n在斐波那契数列的区间
while ((n + 1) > _fibonacci[k])
k++;
// 数组需要扩容
if (n < _fibonacci[k] - 1)
{
// 扩容
newNums = new int[_fibonacci[k]];
for (int i = 0; i <= n; i++)
newNums[i] = nums[i];
}
else
{
newNums = nums;
}
// 若F[k]>n时,避免后面在nums中取值时,数组越界而报错,因此在上面需要对nums进行扩容。
for (int i = n; i < _fibonacci[k] - 1; i++)
newNums[i] = newNums[n];
// 查找核心算法
while (low <= high)
{
// 从low开始,有F[k - 1] - 1个元素,才到mid
mid = low + _fibonacci[k - 1] - 1;
if (key < newNums[mid])
{
high = mid - 1;
k = k - 1;
}
else if (key > newNums[mid])
{
low = mid + 1;
k = k - 2;
}
else
{
if (mid <= n)
return mid;
// 超出的部分就是后面我们自动补全的
else
return n;
}
}
return -1;
}