怎样写出一个正确的二分搜索

来源:互联网 发布:淘宝的客户关系管理 编辑:程序博客网 时间:2024/05/04 22:38

最近在做二分搜索的题目,可是正如在编程珠玑里面所说的一样,正确的二分搜索只有10%的程序员才能够写出来。

去google搜索了一下:如何写出正确的二分搜索,开头就看见了Hacker News上面的文章:

https://news.ycombinator.com/item?id=1277459

于是花了几个小时研究了一下二分搜索。

1:当然最简单的情形是从给定范围内找到指定的数字,下面我所说的各种情况范围两边都是闭区间的,即[low , high],(范围包括low 和 high)

当你要指定其余查找范围的时候可以很容易的转化为两端闭区间,

如(low, high) -> [low + 1, high - 1]      (low, high] -> [low + 1, high]      [low, high) -> [low, high + 1]

那么要查找该范围内的,首先[low, high] (当low == high的时候表示只有一个点的区间,low > high的时候表示空区间)

所以我们的循环条件就应该如此写:  while (low <= high)    (因为是闭区间表示法,不能够写成low < high),当只有一个点的区间查找的时候显然是错误的。

那么查找一个数的二分查找条件就很容易写

当 array[mid] == number_to_find的时候直接返回。

当array[mid] < number_to_find的时候,查找区间就应该更新为low = mid + 1;

当array[mid] > number_to_find的时候,查找区间就应该更新为high = mid - 1;

那么一个简单正确的查找一个数的二分搜索就出来了:

int binarySearchOneNumber(int number_to_find, int low, int high, int *array) {    int mid;    while (low <= high) {        mid = (low + high) / 2;        if (array[mid] == number_to_find) {            return array[mid];        }        else if (array[mid] > number_to_find) {            high = mid - 1;        }        else {            low = mid + 1;        }    }    return -1;}

2和3是同样的情况,即有多个满足条件的数,返回最小的那个,或者最大的那个(以数组元素查找为例子,返回数组下标最小的,或者下标最大的)

下标最小的:

循环条件还是不变,因为我们采取的是闭区间的形式为while(low <= high)

那么当找到一个数满足条件的时候,如array[mid] == number_to_find的时候不能够直接返回。因为有可能存在比mid下标还小的,并且元素值等于number_to_find的元素。

所以我们必须更新high。如果high更新为mid的话,即high = mid;会发生死循环。为什么呢?

(1)假设不再存在满足条件的元素了,那么low一定会逐渐向上更新,更新到和high一样的大小,那么此时就会发生死循环。

(2)如果还存在的话,会被继续划分区间,直到划分成[low, high]里面不存在满足条件的元素,根据(1)会发生死循环。

所以我们应该更新high = mid - 1;

low的更新是毫无疑问的low = mid + 1;

那么我们最后应该返回的是什么呢?

假设我们找到了array[mid] == number_to_find,此时更新high = mid - 1,如果mid就是最小元素的话,那么下一次分的时候一定会逐渐的增加low的值,一直到high - 1,发现

array[high - 1]仍然比number_to_find小,low就会更新为high + 1 = mid,即找到的满足条件的下标。

如果还有满足条件元素的话,同理又会在一次找到一个mid,一直找到最小下标,然后再分为没有满足条件的区间,low一定会被更新为满足条件最小元素下标

所以我们最终返回low即可。

代码:

int binarySearchSmallestNumber(int number_to_find, int low, int high, int *array) {    int mid;    while (low <= high) {        mid = (low + high) / 2;        if (array[mid] >= number_to_find) {            high = mid - 1;        }        else {            low = mid + 1;        }    }    return low;}

同理找到最大元素:

int binarySearchBiggestNumber(int number_to_find, int low, int high, int *array) {    int mid;    while (low <= high) {        mid = (low + high) / 2;        if (array[mid] <= number_to_find) {            low = mid + 1;        }        else {            high = mid - 1;        }    }    return high;}

顺便说一句:

如果没有找到元素的话,那么找最小下标的二分就会返回一个下标k,此时如果将查找数插入K,并且将array[k], array[k + 1].... array[high]依次向后移动的话仍然是一个有序数组。

同理找最大下标的返回下标K,将array[k], array[k - 1].....array[0]向前移动的话仍然是一个有序数组。


原创粉丝点击