目录
一、二分法
1、概念:一种针对有序数据集合的查找算法,查找思想有点类似分治思想。每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为 0。
2、时间复杂度分析:假设数据大小是 n,每次查找后数据都会缩小为原来的一半,也就是会除以 2。最坏情况下,直到查找区间被缩小为空,才停止。
可以看出来,这是一个等比数列。其中 n/2k=1 时,k 的值就是总共缩小的次数。而每一次缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,时间复杂度就是 O(k)。通过 n/2k=1,我们可以求得 k=log2n,所以时间复杂度就是 O(logn)
3、局限
首先,二分查找依赖的是顺序表结构,简单点说就是数组。
其次,二分查找针对的是有序数据:二分查找只能用在插入、删除操作不频繁,一次排序多次查找的场景中。针对动态变化的数据集合,二分查找将不再适用
再次,数据量太小不适合二分查找。
最后,数据量太大也不适合二分查找:二分查找的底层需要依赖数组这种数据结构,而数组为了支持随机访问的特性,要求内存空间连续,对内存的要求比较苛刻
/**
* binary search.
*
* @param array: search array
* @param size: array size
* @param search_value: search value
* @return 1
*/
int binary_search(int array[], int size, int search_value)
{
int low = 0;
int high = size - 1;
int mid = 0;
/* 注意结束条件,不是low<high */
while(low <= high)
{
/* 注意size太大导致溢出,所以不为(high+low)/2 */
mid = low + (high - low) / 2;
if (array[mid] == search_value)
{
return mid;
}
else if (array[mid] < search_value)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return -1;
}
二、二分法变形
1、变形一:查找第一个值等于给定值的元素
如下图如果给定要查找的数为8,变形一要解决的就是找到a[5]位置上的8
/**
* 查找第一个等于给定数值的元素.
*
* @param array: search array
* @param size: array size
* @param search_value: search value
* @return 1
*/
int binary_search_variant1(int array[], int size, int search_value)
{
int low = 0;
int high = size - 1;
int mid = 0;
/* 注意结束条件,不是low<high */
while(low <= high)
{
/* 注意size太大导致溢出,所以不为(high+low)/2 */
mid = low + (high - low) / 2;
if (array[mid] < search_value)
{
low = mid + 1;
}
else if (array[mid] > search_value)
{
high = mid - 1;
}
else
{
if (mid == 0 || (array[mid - 1] != search_value))
{
return mid;
}
else
{
high = mid - 1;
}
}
}
return -1;
}
2、变形二:查找最后一个值等于给定值的元素
/**
* 查找最后一个等于给定数值的元素.
*
* @param array: search array
* @param size: array size
* @param search_value: search value
* @return 1
*/
int binary_search_variant2(int array[], int size, int search_value)
{
int low = 0;
int high = size - 1;
int mid = 0;
/* 注意结束条件,不是low<high */
while(low <= high)
{
/* 注意size太大导致溢出,所以不为(high+low)/2 */
mid = low + (high - low) / 2;
if (array[mid] < search_value)
{
low = mid + 1;
}
else if (array[mid] > search_value)
{
high = mid - 1;
}
else
{
if (mid == (size - 1) || (array[mid + 1] != search_value))
{
return mid;
}
else
{
low = mid + 1;
}
}
}
return -1;
}
3、变形三:查找第一个大于等于给定值的元素
比如,数组中存储的这样一个序列:3,4,6,7,10。如果查找第一个大于等于 5 的元素,那就是 6。
/**
* 查找第一个大于等于给定数值的元素.
*
* @param array: search array
* @param size: array size
* @param search_value: search value
* @return 1
*/
int binary_search_variant3(int array[], int size, int search_value)
{
int low = 0;
int high = size - 1;
int mid = 0;
/* 注意结束条件,不是low<high */
while(low <= high)
{
/* 注意size太大导致溢出,所以不为(high+low)/2 */
mid = low + (high - low) / 2;
if (array[mid] < search_value)
{
low = mid + 1;
}
else
{
if (mid == 0 || (array[mid - 1] < search_value))
{
return mid;
}
else
{
high = mid - 1;
}
}
}
return -1;
}
4、变形四:查找最后一个小于等于给定值的元素
比如,数组中存储了这样一组数据:3,5,6,8,9,10。最后一个小于等于 7 的元素就是 6
/**
* 查找最后一个小于等于给定数值的元素.
*
* @param array: search array
* @param size: array size
* @param search_value: search value
* @return 1
*/
int binary_search_variant4(int array[], int size, int search_value)
{
int low = 0;
int high = size - 1;
int mid = 0;
/* 注意结束条件,不是low<high */
while(low <= high)
{
/* 注意size太大导致溢出,所以不为(high+low)/2 */
mid = low + (high - low) / 2;
if (array[mid] > search_value)
{
high = mid - 1;
}
else
{
if (mid == (size - 1) || (array[mid + 1] > search_value))
{
return mid;
}
else
{
low = mid + 1;
}
}
}
return -1;
}