二分搜索主要解决的问题是确定排序后的数组中是否包含目标元素val。
二分搜索通过持续跟踪数组中包含元素val的范围。分为两个过程,第一就是找到了,第二个就是没找到;
一开始,这个范围是整个数组,然后通过将val与数组中的中间项进行比较并抛弃一半的范围来缩小范围。该过程持续进行。
1、非递归算法
//12 12 12 13 13 13 45 45 56 56 56 78 89 90 100,查询val,且返回最左边(最右边)的val的下标
int BinSearch(const vector<int>& ar, int val)
{
int left = 0, right = ar.size() - 1;
int pos = -1;
while (left <= right)
{
int mid = (right - left) / 2 + left;
if (val < ar[mid])
{
right = mid - 1;
}
else if (val > ar[mid])
{
left = mid + 1;
}
else
{
//while (mid > left && ar[mid - 1] == val) --mid;
//当有多个val值时,找最左边下标时
//while (mid < right && ar[mid + 1] == val) ++mid;
//当有多个val值时,找最右边下标时
pos = mid;
break;
}
}
return pos;
}
编写代码需要注意:
1.循环退出条件注意是left <= right
2.mid 的取值:
注意本代码没有写成mid = (left + right) / 2 ,原因是如果 left 和 right 比较大的话,两者之和就有可能会溢出。具我所知,如果要将性能优化到极致的话,我们可以将这里的除以 2 操作转化成位运算 low+((high-low)>>1),因为相比除法运算来说,计算机处理位运算要快得多。但是本人平常编程基本不用左移右移运算符。
3.left 和 right 的更新
left = mid + 1,rigth = mid - 1。注意这里是 +1 和 -1,即mid位不再参与查询。如果直接写成 low=mid 或者 high=mid,就可能会发生死循环。比如,当 high = mid = low != val 时,就会导致一直循环不退出。
2、递归算法
//递归算法
int BinSearch(const vector<int>& ar, int left, int right, int val)
{
if (left < right)
{
int mid = (right - left) / 2 + left;
if (ar[mid] == val)
return mid;
else if (ar[mid] > val)
return BinSearch(ar, left, mid - 1, val);
else
return BinSearch(ar, mid + 1, right, val);
}
return -1;
}
二分查找算法的局限性:
1、二分查找法依赖的是顺序表结构。因为二分查找方法需要按照下标随机访问元素。
2、二分查找针对的是有序数据。所以二分查找只能在插入、删除操作不频繁,一次排序多次查找的场景中。
3、二分查找依赖于顺序存储结构,要求内存空间连续,如果数据量太过大的情况下,可能存在空间不够分配的困难。