二分法查找是比较常见的查询算法。
对于一个有序序列,注意:是有序序列,找出其中某一个元素,通常的做法是序列中拿出一个一个的元素,每个元素和所需元素作比较,如果是想要的结果,就把元素的序号输出来。例如有序列:0, 3, 5, 7, 9, 11, 12, 13。我想要找7这个元素,我就取第一个元素0,和“11”比较,发现不是,然后取第二个“3”和“11”比较...直到取到第六个元素“11”,发现是想要的,就输出6。这样查询的时间复杂度很明显是N。
这样做显然不够完美,写程序是讲究运行效率的。以上面的序列“0, 3, 5, 7, 9, 11, 12, 13”为例,二分法的做法是:首先确定查找的“区间”,区间左端是“0”,区间右端是“13”,然后取这个区间中点作为和需要比较的对象比较,如果中点元素大于要查对象,就把中点作为下次查找的区间的左端(以从小到大的序列为例,如果序列是从大到小反之为右端),原来的右端还是右端;如果中点元素小于要查的对象,就把中点作为下次查找的区间的右端...然后继续这样比较,直到查到所查元素为止。这样查询的时间复杂度是log2N。可能很多人对于这个结果的所得表示很茫然,让我们来一起算一下。假设序列的长度是N,那么第一次查找后变为:N/2,第二次查找后:N/4...假设第x次 后找到所选数字:N/(2^x);这个时候序列的长度应该是大于或者等于1的;即2^x >= N ,由于算的是x的次数,可令 2^x = N ;得出:x = log2N 即时间复杂度为:log2N。如果这样不是很好理解,我们换一个思维想想:
时间复杂度不就是循环的次数嘛,求时间复杂度实际上就是求循环了多少次呗~我们倒着推:假设现在已经取到所需的元素,那么现在的序列长度肯定是大于等于一,就令他为一,时间复杂度嘛,为的就是算最长需要的时间嘛,它现在乘以2就是上一次的长度,再乘以2就是上上此的长度,假设成了x次后变为了N,那么为: 2^x = N。循环次数x为log2N,即时间复杂度为:log2N。
以下分别是用Golang、PHP和Scala写的二分法查找:
Golang:
//输出-1代表没有此数字 func binarySearch(arry [8]int, val int) int { left := 0 right := len(arry) - 1 for left < right { var mid int = (right-left)/2 + left if val < arry[mid] { right = mid } else if left > arry[mid] { val = mid } else { return mid + 1 } } return -1 }
PHP:
Scala:function binary_search(array $array, $val){ $left = 0; $right = count($array)-1; while($left < $right){ $mid = floor(($right - $left)/2) + $left; if($val > $array[$mid]){ $right = $mid; } else if ($val < $array[$mid]){ $left =$mid; } else{ return $mid + 1; } return false; } }
def binarySearch(arry:Array[Int], needed:Int):Int={ var left = 0 var right = arry.length -1 while(left < right) { val mid:Int = (right - left)/2 + left needed match { case _ if needed > arry.apply(mid) => left = mid case _ if needed < arry.apply(mid) => right = mid case _ if needed == arry.apply(mid) => return mid + 1 } } return -1 }