二分查找算法

时间:2021-05-17 04:08:34

算法概述

  当数据量很大时适宜采用二分法查找,其是一种效率较高的查找方法,但前提条件是要查找的集合必须是有序的,或是升序排列或是降序排列都可以。二分法又称折半查找,故名思意就是就是从中间开始比较查找,其基本思路是:假设数据是按升序排序的,对于给定值 x,从序列的中间位置开始比较,如果当前位置值等于 x,则查找成功;若 x 小于当前位置值,则在数列的前半段中查找;若 x 大于当前位置值则在数列的后半段中继续查找,直到找到为止。所以二分法查找的速度比较快,次数比较少,性能比较好;因此相对来说其删除和插入操作就不是那么灵活了。

二分查找的优点和缺点

  虽然二分查找的效率高,但是要将表按关键字排序。而排序本身是一种很费时的运算。既使采用高效率的排序方法也要花费O(nlgn)的时间。
  二分查找只适用顺序存储结构。为保持表的有序性,在顺序结构里插入和删除都必须移动大量的结点。因此,二分查找特别适用于那种一经建立就很少改动、而又经常需要查找的线性表。
  对那些查找少而又经常需要改动的线性表,可采用链表作存储结构,进行顺序查找。链表上无法实现二分查找。

二分查找算法的效率(性能)

1000个数据,约10次找出,
100万个数据,约20次找出
10亿个数据,约30次找出
40亿个数据,约32次找出
2^n个数据,约n次找到

算法具体实现

比如有一组数,用二分法查找某个数。
1,3,11,18,19,22,25,33,34,38,44,55,56,58,60,63,66,70,77,87,90,91,92,95,97

下面PHP代码使用递归方式实现二分法查找:

<?php
$a = array(1,3,11,18,19,22,25,33,34,38,44,55,56,58,60,63,66,70,77,87,90,91,92,95,97);
$search1 = 33; //要查找的数据
$search2 = 59;
$len = count($a);

//从数组$arr中的位置$begin开始到位置$end之间找数据$target
function binary_search($arr, $target, $begin, $end){
$mid = floor( ($begin + $end)/2 ); //定中间位置
$mid_value = $arr[$mid]; //取得中间项的值
if($target == $mid_value){
return true;
}else if($target < $mid_value){ //目标值小于中间值
if($begin > $mid-1){ //如果开始位置都比结束位置大,表示肯定找不到了
return false;
}
$result = binary_search($arr, $target, $begin, $mid-1);//末尾索引,前移到$mid-1,右边不再找
}else{ //目标值大于中间值
if($mid+1 > $end){ //如果开始位置都比结束位置大,表示肯定找不到了
return false;
}
$result = binary_search($arr, $target, $mid+1, $end);//前端索引,后移到$mid-1,左边不再找
}
return $result;
}

//使用binary_search()函数从 $a 中的 0 到 len-1 位置找 $search
$v1 = binary_search($a, $search1, 0, $len-1);
$v2 = binary_search($a, $search2, 0, $len-1);
echo "search1的结果为:";var_dump($v1);
echo "search2的结果为:";var_dump($v2);
?>

运行结果为:

search1的结果为:

boolean true

search2的结果为:

boolean false

使用非递归实现二分法

// 非递归算法
function binary_search(arr, key) {
var low = 0,
high = arr.length - 1;
while(low <= high){
var mid = parseInt((high + low) / 2);
if(key == arr[mid]){
return mid;
}else if(key > arr[mid]){
low = mid + 1;
}else if(key < arr[mid]){
high = mid -1;
}else{
return -1;
}
}
};
var arr = [1,2,3,4,5,6,7,8,9,10,11,23,44,86];
var result = binary_search(arr,10);
alert(result); // 9 返回目标元素的索引值

Java代码

使用递归实现二分法查找

class Program
{
static void Main(string[] args)
{
int[] ary = new int[] {1,3,4,6,11,20,24};
int result = Find(ary,6);
Console.WriteLine(result);
}
private static int Find(int []ary,int target)
{
int startIndex = 0;
int endIndex = ary.Length - 1;
int targetIndex = -1;
//判断初始索引与结束索引的大小,若初始索引大于结束索引则说明数组中没有要查找的数
//并且返回-1
while (true)
{
//求中值索引
int MiddleIndex = (startIndex + endIndex) / 2;
//定义中值并初始化为数组中索引为中值索引位置的上数
int Middle = ary[MiddleIndex];
//如果初始索引大于等于结束索引就立刻结束程序
if (startIndex > endIndex)
{
break;
}
//判断目标与中值的大小,如果目标等于中值,并得到索引为middleindex位置上的数后返回该索引
if (target == Middle)
{
targetIndex = MiddleIndex;

break;
}
//如果目标小于中值,那么一定意味着中值(包含中值)以后的数都比目标大,故缩小查找范围
//将结束索引变为种植索引-1
else if (target < Middle)
{
endIndex = MiddleIndex - 1;
}
//如果目标大于中值,那么一定意味着中值(包含中值)以前的数都比目标大,故缩小查找范围
//将初始索引变为种植索引+1
else
{
startIndex = MiddleIndex + 1;
}
}
return targetIndex;
}

使用循环语句实现二分法查找

class Program
{
static void Main(string[] args)
{
int[] ary = new int[] {1,3,4,6,11,20,24};
int result = Find(ary,6);
Console.WriteLine(result);
}
private static int Find(int []ary,int target)
{
int startIndex = 0;
int endIndex = ary.Length - 1;
int targetIndex = -1;
//判断初始索引与结束索引的大小,若初始索引大于结束索引则说明数组中没有要查找的数
//并且返回-1
while (true)
{
//求中值索引
int MiddleIndex = (startIndex + endIndex) / 2;
//定义中值并初始化为数组中索引为中值索引位置的上数
int Middle = ary[MiddleIndex];
//如果初始索引大于等于结束索引就立刻结束程序
if (startIndex > endIndex)
{
break;
}
//判断目标与中值的大小,如果目标等于中值,并得到索引为middleindex位置上的数后返回该索引
if (target == Middle)
{
targetIndex = MiddleIndex;

break;
}
//如果目标小于中值,那么一定意味着中值(包含中值)以后的数都比目标大,故缩小查找范围
//将结束索引变为种植索引-1
else if (target < Middle)
{
endIndex = MiddleIndex - 1;
}
//如果目标大于中值,那么一定意味着中值(包含中值)以前的数都比目标大,故缩小查找范围
//将初始索引变为种植索引+1
else
{
startIndex = MiddleIndex + 1;
}
}
return targetIndex;
}