前几天面试的时候遇到了这个问题 ,标准库下提供的二分查找改错,当时没有改出来,写得不好,回来查了下,这个函数的原型是:
/* bsearch() and qsort() are declared both here, in <stdlib.h>, and in
* non-ANSI header <search.h>; we reproduce these declarations in both,
* with no attempt to guard them, so the compiler may verify that they
* are consistent, if both headers are included.
*/
_CRTIMP __cdecl void *bsearch
(const void *, const void *, size_t, size_t, int (*)(const void *, const void *));
接受任何类型的数据进行二分查找,自己一开始纠结在于返回值问题,一直以来自己以为的查找局限于,数组,返回的应该是一个下标,查到的元素在数组中的第几个。这是自己一点的误区,应该联想内存一起思考。
回来看了下bsearch源码,发现不然,标准库提供的二分查找,返回的是内存地址,而不是一个下标更不是一个值。
返回的是一个内存地址,这样子这个二分查找是非常强大的,可以对一段连续内存进行查找,前提这段内存的数据是有序的,。
看一下标准库的二分查找实现代码。
/* Perform a binary search for KEY in BASE which has NMEMB elements
of SIZE bytes each. The comparisons are done by (*COMPAR)(). */
void *
bsearch (const void *key, const void *base, size_t nmemb, size_t size,
int (*compar) (const void *, const void *))
{
size_t l, u, idx;
//这里的idx就是查找的位置 但返回的并不是idx idx只是用来计算距离首地址有多少个元素
const void *p;
// 这个p代表的是 idx处的值
int comparison;
// 初始化左右边界
l = 0;
u = nmemb;
while (l < u)
{
// 取左右区间的中点
idx = (l + u) / 2;
// base + (idx * size)这个是计算idx处的内存地址地址 传递给compar函数进行值比较
p = (void *) (((const char *) base) + (idx * size));
comparison = (*compar) (key, p);
if (comparison < 0)
// 右边界更新为当前中点
u = idx;
else if (comparison > 0)
// 左边界右移动一个
l = idx + 1;
else
return (void *) p;
}
return NULL;
}
关于对标准库的二分查找,要从内存角度理解,就如同void*类型的qsort一样。不单单停留在值的层次,是从内存的角度思考出发。
而对这个返回的内存地址,和首地址做差,就能得到偏移的字节数,进一步可以得到下标
index = (pes - (void *)array) / sizeof(array[0]);