算法简介
算法前提
二分查找法主要是解决在“一堆数中找出指定的数”这类问题。
而想要应用二分查找法,这“一堆数”必须有一下特征:
- 存储在数组中
- 有序排列
所以如果是用链表存储的,就无法在其上应用二分查找法了。
至于是顺序递增排列还是递减排列,数组中是否存在相同的元素都不要紧。不过一般情况,我们还是希望并假设数组是递增排列,数组中的元素互不相同。
基本思想
二分查找法在算法家族大类中属于“分治法”,分治法基本都可以用递归来实现的。
不过所有的递归都可以自行定义stack来解递归,所以二分查找法也可以不用递归实现,而且它的非递归实现甚至可以不用栈,因为二分的递归其实是尾递归,它不关心递归前的所有信息。
步骤:
- 由边界low、high,求出中间下标mid;
- 比较array[mid]与目标值,由结果缩小范围;
- 找到则退出,未找到重复1、2步骤直到 范围size为1且仍不为目标值。
代码实现
#include <stdio.h>
/*
非递归方式实现对有序表的二分查找,找到返回下标
array[]:有序表
low:低位
high:高位
target:目标元素
*/
int BinSearchWithoutRecursion(int array[], int low, int high, int target)
{
while (low <= high)
{
int mid = (low + high) / 2;
if (array[mid] > target)//中值大于目标值,则中值为高位
{
high = mid - 1;
}
else if (array[mid] < target)//中值小于目标值,则中值为低位
{
low = mid + 1;
}
else
{
return mid; //找到返回
}
}
return -1; //没找到返回
}
/*
递归方式实现对有序表的二分查找,找到返回下标
array[]:有序表
low:低位
high:高位
target:目标元素
*/
int BinSearchWithRecursion(int array[], int low, int high, int target)
{
if (low <= high)
{
int mid = (low + high) / 2;
if (array[mid] > target)
{
return BinSearchWithRecursion(array, low, mid - 1, target);
}
else if (array[mid] < target)
{
return BinSearchWithRecursion(array, mid + 1, high, target);
}
else
{
return mid;
}
}
return -1;
}
int main()
{
int array[6] = { 1,2,3,4,5,6 };
int index = BinSearchWithoutRecursion(array, 0, 5, 2);
printf("位置为%d,值为%d\n", index, array[index]);
index = BinSearchWithRecursion(array, 0, 5, 2);
printf("位置为%d,值为%d\n", index, array[index]);
return 0;
}
运行结果:
复杂度分析
二分查找的时间复杂度即为while循环的次数。
总共有n个元素, 渐渐跟下去就是n,n/2,n/4,….n/2^k,其中k就是循环的次数 。
由于你n/2^k取整后>=1,
可得k=log2n,(是以2为底,n的对数)
所以时间复杂度可以表示O()=O(logn)
缺陷
二分查找法的O(log n)让它成为十分高效的算法。不过它的缺陷却也是那么明显的。就在它的限定之上:
必须有序,我们很难保证我们的数组都是有序的。当然可以在构建数组的时候进行排序,可是又落到了第二个瓶颈上:它必须是数组。
数组读取效率是O(1),可是它的插入和删除某个元素的效率却是O(n)。因而导致构建有序数组变成低效的事情。
解决这些缺陷问题更好的方法应该是使用二叉查找树了,最好自然是自平衡二叉查找树了,既能高效的(O(n log n))构建有序元素集合,又能如同二分查找法一样快速(O(log n))的搜寻目标数。
原文地址:二分查找法的实现和应用汇总
如果有什么错误之处或建议,请在评论区告诉我,谢谢!