Я рассматриваю следующую проблему,
Учитывая отсортированный массив размера n, содержащий целые числа без дубликатов, можем ли мы добиться большего успеха, чем обычный двоичный поиск, используя свойство, которое
- a) нет дубликатов
- b) между двумя смежными целыми числами нет целых чисел (т. е. после 50 либо есть 51, либо 51 нет в массиве)
Идея в том, что когда вы встречаете значение, вы добавляете тест, чтобы увидеть, является ли искомое значение смежным с текущим значением (+ или -1), если да, то интервал поиска вместо того, чтобы делиться пополам, уменьшается до одной точки, индекса рядом с текущей серединой.
Например, предположим, что у вас есть массив tab [i] = i для всех индексов и со всеми значениями от 0 до 99. Мы ищем 51, первая середина равна 50, поэтому нормальный бинарный поиск в худшем случае в 7 попаданиях (log2 (100)). С помощью дополнительного теста мы тестируем 50 и сокращаем интервал поиска до соседа 50, так что конечное значение sh за два шага (но с добавленным тестом).
Это один пример, но он не является репрезентативным моего набора данных, другой пример может быть {0,13223,13225,42341,42342} или любой набор значений, отсортированных без повторов. Просто чтобы дать некоторый контекст, эти массивы, которыми я манипулирую, являются ключами (непустыми индексами) в реализации разреженного массива.
В худшем случае кажется, что мы заканчиваем, когда интервал равен размеру 3, а не 2, так что log2 (n) -1 тестирует.
В коде это даст что-то вроде (Java используется здесь), вызов с 0 в качестве lo и длиной массива-1 в качестве hi для поиска во всем массиве:
// This is Arrays.binarySearch(), but doesn't do any argument validation.
static int binarySearchGT(int[] array, int value, int lo, int hi) {
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
становится
static int binarySearch(int[] array, int value, int lo, int hi) {
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
if (hi != mid && midVal == value -1) {
hi = mid + 1;
}
lo = mid + 1;
} else if (midVal > value) {
if (lo != mid && midVal == value + 1) {
lo = mid - 1;
}
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
Верны ли мои рассуждения, полагая, что это должно (всегда) быть лучше, чем обычный двоичный поиск в этом конкретном дискретном / неповторяющемся случае входов? Я вижу, что у меня есть дополнительная ветвь и два булевых теста, включая дополнение, но все еще с большими входами, можете ли вы показать случай, когда эта стратегия явно худшая?
Кто-нибудь знает ссылку на какой-то вид подобной идеи в литературе?
[Отредактировано, чтобы лучше объяснить, не все элементы присутствуют]