#c #google
Несколько дней назад мне понадобилось искать в программе на Си строки с заданным
префиксом в отсортированном массиве. Очевидно, что родной bsearch для этого не совсем
пригоден, поскольку для нескольких совпадений он находит произвольное.
Почему-то я решил поискать код, решающий эту проблему в сети (через google). Естественно,
мне тут же попались equal_range(), lower_bound и upper_bound из C++. Но мне то хотелось
найти готовый эквивалент equal_range именно на Си и просто скопипастить его.
Не тут то было... Поискав полчасика и вдоволь насмотревшись на разные варианты реализации
поиска пришлось делать самому. Потом я (уже чисто из "спортивного интереса") еще несколько
раз пытался найти такую программу, но безуспешно.
Интересно, это я гуглить не умею или такую простую штуку никто не удосужился выложить
в сеть?
Вот код. Может кому-нибудь пригодиться.
void *
bsearcher (const void *key, const void *base, size_t nmemb, size_t size,
int (*comp)(const void *key, const void *item), size_t *ipos)
{
if (ipos)
*ipos = 0;
if (!nmemb || !size)
return 0;
size_t first = 0, last = nmemb, mid, res;
while (first < last) {
mid = first + (last - first) / 2; // by reason of overflow!!!
if (comp(key, (char *)base + mid * size) > 0)
first = mid + 1;
else
last = mid;
}
if ((res = first++) < nmemb && comp(key, (char *)base + res * size) == 0) {
// puts("first key FOUND!"); // yes, it's the comment (just unusual form)
if (first < nmemb) {
if (comp(key, (char *)base + first * size) == 0) {
// puts ("Have more..., looking for last");
last = nmemb;
while (first < last) {
mid = first + (last - first) / 2;
if (comp(key, (char *)base + mid * size) < 0)
last = mid;
else
first = mid + 1;
}
}
if (ipos)
*ipos = first;
} else {
// puts ("last in array");
if (ipos)
*ipos = nmemb;
}
} else {
// puts("Not Found!");
if (ipos)
*ipos = res;
return 0;
}
return (void *)((char *)base + res * size);
}
А здесь, в пастебине он же с тестовым примерчиком.
Функция почти совпадает с bsearch (man 3 bsearch), но всегда (если ключ найден) возвращает
указатель на первое его вхождение в массив, плюс в последнем параметре возвращается
индекс в массиве после последнего вхождения ключа в массив (т.е. мы получаем искомый
диапазон). Если ключ не найден, то в этом параметре будет индекс элемента массива в
который можно вставлять искомый ключ.
Update
Для полноты решил добавить (в пастебине тоже отредактировал) пару функций:
bsearch_lb() и bsearch_ub() для поиска первого и последнего вхождения в массив ключа,
соответственно. Просто каждая из них выполняет свою задачу чуть более эфеективно, чем
более общая bsearcher() (а возможно, кому-то просто будет интересно посмотреть на алгоритмы
поиска в "чистом виде").
Если ключ найден, то bsearch_lb() в аргументе ipos возвращает позицию (индекс в массиве)
найденного элемента. В остальном поведение этих функций аналогично bsearcher().
void *
bsearch_lb (const void *key, const void *base, size_t nmemb, size_t size,
int (*comp)(const void *key, const void *item), size_t *ipos)
{
if (ipos)
*ipos = 0;
if (!nmemb || !size)
return 0;
size_t first = 0, last = nmemb, mid;
while (first < last) {
mid = first + (last - first) / 2; // by reason of overflow!!!
if (comp(key, (char *)base + mid * size) > 0)
first = mid + 1;
else
last = mid;
}
if (ipos)
*ipos = first;
if (first < nmemb && comp(key, (char *)base + first * size) == 0)
return (void *)((char *)base + first * size);
return 0;
}
void *
bsearch_ub (const void *key, const void *base, size_t nmemb, size_t size,
int (*comp)(const void *key, const void *item), size_t *ipos)
{
if (ipos)
*ipos = 0;
if (!nmemb || !size)
return 0;
size_t first = 0, last = nmemb, mid;
while (first < last) {
mid = first + (last - first) / 2;
if (comp(key, (char *)base + mid * size) < 0)
last = mid;
else
first = mid + 1;
}
if (ipos)
*ipos = first;
if (first && comp(key, (char *)base + --first * size) == 0)
return (void *)((char *)base + first * size);
return 0;
}
Конечно, это сообщение не похоже на вопрос и ответов не требует. Возможно, его место
в исследованиях (хотя, исследованием тут тоже и не пахнет).
Ответы
Ответ 1
Посмотри любую!!! реализацию бинарного поиске не через три пути (меньше , равно , больше) - (начни с Бентли Жемчужины программирования) а через два пути меньше и больше где равно прислоняют к одному из вариантов а условие выхода единственное когда границе перестают быть инвариантом ( l
Комментариев нет:
Отправить комментарий