基本思路
当我们要从一个序列中查找一个元素的时候,最快想到的方法就是顺序查找法(即:从前到后依次查找)。但这种方法过于无脑,就是暴力的把每个元素都排查一遍。元素个数少的时候还行,一旦元素个数多起来,效率是非常低下,所以在实际中这种查找的方法是被摒弃的。
这里就不得不介绍一种简单且效率较高的查找方法了:二分查找法,又称折半查找法。但该方法是建立在有序的前提下的,基本思路就是:
先找到那个有序序列的中间元素mid,然后拿它和要找的元素K进行比较,就可以初步判断元素K所在范围,既然查找范围已确定,自然该范围之外的元素就可以不用再查找了(你看这样相较于顺序查找一下子就可以省略一半的元素不用查找了,这就是效率啊!)。当然接下来还会按照上面的步骤反复查找下去。
因为二分查找每一次查找都可以缩减掉一半的查找范围,由此可以知道二分查找法的时间复杂度是: log 2 ( N ) 。举个例子来解释该时间复杂度:若这里一共有2^32个元素,那么我在最坏的情况下也只需要32次就可以找到我想找的元素;而顺序查找法最坏的情况下,却需要查找 4,294,967,296 次!!!,可见二分查找法的效率是非常之高的。
前提条件
都说二分查找法的前提条件是:查找的序列必须是有序的。我想大概很多小伙伴都会错误的认为有序是差值恒为1的顺序数列(就像这样1、2、3、4、5、6、7、8、9)。这只不过是有序的某一种情况罢了,那何为有序呢?即该序列中的所有元素都是按照升序或者降序排列好的,元素与元素只间的差值虽然是随机的,但始终是在递增或递减。
除此之外,二分查找只能实现单值查找,不可能实现多值查找!
时间复杂度
时间复杂度指出了算法有多快。例如,假设列表包含n个元素。简单查找需要检查每个元素,因此需要执行n次操作。使用时间复杂度,这个运行时间为O(n)。单位秒呢?没有——时间复杂度指的并非以秒为单位的速度。时间复杂度让你能够比较操作数,它指出了算法运行时间的增速。
二分查找的时间复杂度如图所示:
下面介绍一些常见算法的时间复杂度:
O(log n),也叫对数时间,这样的算法包括二分查找。
O(n),也叫线性时间,这样的算法包括简单查找。
O(n * logn),这样的算法包括第4章将介绍的快速排序——一种速度较快的排序算法。
O(n2 ),这样的算法包括第2章将介绍的选择排序——一种速度较慢的排序算法。
O(n!),这样的算法包括接下来将介绍的旅行商问题的解决方案——一种慢的算法。
代码实例
C语言实现
#include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//存储递增的有序数列
int sz = sizeof(arr) / sizeof(arr[0]);//sz是数组元素的总个数
int mid = 0;//存储中间元素的下标
//左、右元素下标确定了被查找元素k的所在范围
int left = 0;//最左边元素的下标
int right = sz - 1;//最右边元素的下标
int k = 0;//所要查找的元素k
printf("请输入所要查找的数:");
scanf("%d", &k);
while (left <= right)
{
mid = (left + right) / 2;//计算中间元素的下标
//判断mid和看大小,重新精确k所在范围
if (arr[mid] < k)
{
left = mid + 1;
//如果中间的数字小于目标值,则中间数字向左的所有数字都小于目标值,全部排除
}
else if (arr[mid] > k)
{
right = mid - 1;
//如果中间的数字大于目标值,则中间数字向右的所有数字都大于目标值,全部排除
}
else
{
break;
}
}
if (left > right)
printf("没有找到该数\n");
else
{
printf("找到了\n");
}
}
Python实现
nums = [-2,3,4,5,6,9]
def er_search_(alist,item):
left,right=0, len(alist)-1
while left<=right:
mid = (left+right)//2
if alist[mid] < item:
left = mid + 1
elif alist[mid] > item:
right = mid - 1
else:
return mid
return -1
er_search_(nums,6)
4
er_search_(nums,9)
5
LeetCode算法题
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例:
输入: nums = [1,3,5,6], target = 5
输出: 2
int searchInsert(int* nums, int numsSize, int target){
int l=0, r= numsSize-1;
int ans = numsSize;
while(l <= r){
int mid = (l+r)/2;
if(nums[mid] >= target){
ans = mid;
r = mid - 1;
}
else {
l = mid +1;
}
}
return ans;
}