二分查找(binary search)
二分查找对于学过数据结构或者算法的人来说,应该是非常熟悉的。其基本思想是:比较目标(target)和中间关键字的大小关系,如果二者相等,则查找完毕;如果二者不等,则可以根据二者的大小关系,将查找的范围减半。
虽然二分查找的算法很简单,但是对于一个code新手来说,正确无误地实现一个二分查找算法也不是那么容易的。
最近,刷了几道二分查找的题目,将刷题的一点收获整理如下:
1.二分查找算法适用的范围:
①必须是顺序表,不适用于链式存储
②查找之前,序列必须是有序的。对于无序的序列,可以先采用合适的排序算法进行排序后,再使用二分查找。
2.二分查找算法的基本模式:
递归方式:
int binary_search(const int[] arr,int start,int end,int khey){
if(start > end)
return -1;
int mid = start + (end - start) / 2;
if(arr[mid] > khey)
return binary_search(arr,start,mid-1,khey);
if(arr[mid] < khey)
return binary_search(arr,mid+1,end,khey);
return mid;//最后检测相等是因为大多数情况下是小于或者大于
}
非递归方式:(更常用)
int binary_search(const int[] arr,int start,int end,int khey){
int mid;
while(start <= end){
mid = start + (end - start) / 2;
if(arr[mid] < khey)
start = mid +1;
else if(arr[mid] > khey)
end = mid - 1;
else
return mid;
}
return -1;
}
其中,需要注意的地方有:
① 我们学习二分算法的时候,求mid可能会用:mid = (start + end)/2,这时,在计算start+end的时候,可能会产生溢出,故以上所写的两种方式,均采用start + (end - start) / 2的方式,有时候可以避免溢出。
②在非递归算法中,while循环中究竟应该是(start < end)还是(start <= end),这应该具体情况具体分析;
③对于mid和khey比较的三种情况(大于,小于,相等)的排列顺序,大多数情况下,是影响不大的,实际中,可以根据三种情况出现的概率,决定其顺序;
④以上仅仅是二分查找算法的模板,具体使用时应该具体情况具体分析,灵活运用。
3.二分查找的复杂度
时间复杂度:O(logn)
空间复杂度:O(1)
在有些地方你可能会看到三分查找(ternary search),三分查找基本思想和二分查找一致,具体来讲:每次选出三分之一点和三分之二点,而不是中点,然后比较target和这两个点的大小关系,从而将查找的范围缩小至原来的三分之一。
二分查找和三分查找的空间复杂度都是O(1),而二分查找的时间复杂度是O(log2n),三分查找的时间复杂度是O(log3n)。既然三分查找的时间复杂度要优于二分查找,那我们为什么不经常使用三分查找,而经常使用二分查找呢?原因在于在最坏情况下,三分查找要明显劣于二分查找。这个具体的推导过程就不详述了, 想了解的可以参考这道题目。