Недавно у меня было интервью, где мне задали « поисковый » вопрос.
Вопрос был:
Предположим , что существует массив (положительных) целых чисел, из которых каждый элемент является либо
+1
или по-1
сравнению с его соседними элементами.Пример:
array = [4,5,6,5,4,3,2,3,4,5,6,7,8];
Теперь найдите
7
и верните его позицию.
Я дал такой ответ:
Сохраните значения во временном массиве, отсортируйте их, а затем примените двоичный поиск.
Если элемент найден, верните его позицию во временном массиве.
(Если число встречается дважды, верните его первое вхождение)
Но, похоже, их не удовлетворил этот ответ.
Каков правильный ответ?
Ответы:
Вы можете выполнить линейный поиск с шагами, которые часто больше 1. Важное наблюдение состоит в том, что если eg
array[i] == 4
и 7 еще не появилось, то следующий кандидат на 7 находится по индексуi+3
. Используйте цикл while, который многократно переходит непосредственно к следующему жизнеспособному кандидату.Вот несколько обобщенная реализация. Он находит первое вхождение
k
в массиве (с учетом ограничения + = 1) или,-1
если оно не встречается:#include <stdio.h> #include <stdlib.h> int first_occurence(int k, int array[], int n); int main(void){ int a[] = {4,3,2,3,2,3,4,5,4,5,6,7,8,7,8}; printf("7 first occurs at index %d\n",first_occurence(7,a,15)); printf("but 9 first \"occurs\" at index %d\n",first_occurence(9,a,15)); return 0; } int first_occurence(int k, int array[], int n){ int i = 0; while(i < n){ if(array[i] == k) return i; i += abs(k-array[i]); } return -1; }
выход:
7 first occurs at index 11 but 9 first "occurs" at index -1
источник
O(N)
, но я не думаю, что есть более быстрый способ сделать это.Ваш подход слишком сложен. Вам не нужно изучать каждый элемент массива. Первое значение
4
, так7
это по крайней мере7-4
элементов прочь, и вы можете пропустить их.#include <stdio.h> #include <stdlib.h> int main (void) { int array[] = {4,5,6,5,4,3,2,3,4,5,6,7,8}; int len = sizeof array / sizeof array[0]; int i = 0; int steps = 0; while (i < len && array[i] != 7) { i += abs(7 - array[i]); steps++; } printf("Steps %d, index %d\n", steps, i); return 0; }
Вывод программы:
Steps 4, index 11
Изменить: улучшено после комментариев от @Raphael Miedl и @Martin Zabel.
источник
if ((skip = 7 - array[i]) < 1) skip = 1;
мой взгляд, придирка, кажется, слишком усложняет и пессимизирует. Еслиarray[i] == 200
вы получаете-193
и просто пропускаете 1 каждый раз, даже если можете пропустить все 193. Почему не простоi += abs(7 - array[i])
?skip
абсолютную разницу между 7 иarray[i]
.200
, ты бы прошел7
.+1
/ находятся-1
друг от друга. Так что это могло быть просто,array[0] == 200
а остальные в основном-1
российские.Вариант обычного линейного поиска может быть хорошим вариантом. Давайте выберем элемент, скажем
array[i] = 2
. Теперьarray[i + 1]
будет либо 1, либо 3 (нечетное число),array[i + 2]
либо (только положительные целые числа) 2 или 4 (четное число).Если продолжать так, можно наблюдать закономерность -
array[i + 2*n]
будут содержать четные числа, поэтому все эти индексы можно игнорировать.Также мы видим, что
array[i + 3] = 1 or 3 or 5 array[i + 5] = 1 or 3 or 5 or 7
таким образом,
i + 5
следует проверить индекс, и цикл while может использоваться для определения следующего индекса для проверки, в зависимости от значения, найденного в индексеi + 5
.Хотя у этого есть сложность
O(n)
(линейное время с точки зрения асимптотической сложности), с практической точки зрения это лучше, чем обычный линейный поиск, поскольку не все индексы посещаются.Очевидно, все будет наоборот, если
array[i]
(наша отправная точка) была нечетной.источник
Подход, представленный Джоном Коулманом, по всей вероятности, является тем, на что надеялся интервьюер.
Если вы хотите пойти немного сложнее, вы можете увеличить ожидаемую длину пропуска:
вызовите целевое значение k . Начните со значения v первого элемента в позиции p и вызовите разность kv dv с абсолютным значением av . Чтобы ускорить отрицательный поиск, взгляните на последний элемент как на другое значение u в позиции o: если dv × du отрицательно, k присутствует (если любое вхождение k допустимо, вы можете сузить здесь диапазон индекса, как это делает двоичный поиск). Если av + au больше, чем длина массива, k отсутствует. (Если DV × ди равен нулю, v или и равен к.)
Опуская индекс достоверности: зондировать ( «следующий») положение , в котором последовательность может вернуться к V с к в середине:
o = p + 2*av
.Если dv × du отрицательно, найти k (рекурсивно?) От p + av до o-au;
если он равен нулю, u равно k в точке o.
Если du равно dv и значение в середине не k, или au превышает av,
или вы не можете найти k от p + av до o-au,
позвольте
p=o; dv=du; av=au;
и продолжайте зондирование.(Чтобы полностью вернуться к текстам 60-х годов, просмотрите с помощью Courier. Моей «первой второй мыслью» было использовать
o = p + 2*av - 1
, что исключает du равно dv .)источник
ШАГ 1
Начните с первого элемента и проверьте, 7. Допустим,
c
это индекс текущей позиции. Итак, изначальноc = 0
.ШАГ 2
Если это 7, вы нашли индекс. Это
c
. Если вы достигли конца массива, вырвитесь.ШАГ 3
Если это не так, то 7 должны быть по крайней мере на
|array[c]-7|
расстоянии друг от друга, потому что вы можете добавить только единицу для каждого индекса. Поэтому добавьте|array[c]-7|
к вашему текущему индексу c и снова перейдите к ШАГУ 2 для проверки.В худшем случае, когда есть чередующиеся 1 и -1, временная сложность может достигать O (n), но средние случаи будут доставлены быстро.
источник
|c-7|
где,|array[c]-7|
кажется,array[c]-7
может быть положительным или отрицательным. Вам нужно обратитьсяabs()
к нему, прежде чем переходить вперед.array[c] - 7
с оператором модуля|array[c] - 7|
.Здесь я даю реализацию на java ...
public static void main(String[] args) { int arr[]={4,5,6,5,4,3,2,3,4,5,6,7,8}; int pos=searchArray(arr,7); if(pos==-1) System.out.println("not found"); else System.out.println("position="+pos); } public static int searchArray(int[] array,int value) { int i=0; int strtValue=0; int pos=-1; while(i<array.length) { strtValue=array[i]; if(strtValue<value) { i+=value-strtValue; } else if (strtValue==value) { pos=i; break; } else { i=i+(strtValue-value); } } return pos; }
источник
Вот решение в стиле «разделяй и властвуй». За счет (гораздо) большего объема бухгалтерского учета мы можем пропустить больше элементов; Вместо того, чтобы сканировать слева направо, тестируйте в середине и пропускайте в обоих направлениях.
#include <stdio.h> #include <math.h> int could_contain(int k, int left, int right, int width); int find(int k, int array[], int lower, int upper); int main(void){ int a[] = {4,3,2,3,2,3,4,5,4,5,6,7,8,7,8}; printf("7 first occurs at index %d\n",find(7,a,0,14)); printf("but 9 first \"occurs\" at index %d\n",find(9,a,0,14)); return 0; } int could_contain(int k, int left, int right, int width){ return (width >= 0) && (left <= k && k <= right) || (right <= k && k <= left) || (abs(k - left) + abs(k - right) < width); } int find(int k, int array[], int lower, int upper){ //printf("%d\t%d\n", lower, upper); if( !could_contain(k, array[lower], array[upper], upper - lower )) return -1; int mid = (upper + lower) / 2; if(array[mid] == k) return mid; lower = find(k, array, lower + abs(k - array[lower]), mid - abs(k - array[mid])); if(lower >= 0 ) return lower; upper = find(k, array, mid + abs(k - array[mid]), upper - abs(k - array[upper])); if(upper >= 0 ) return upper; return -1; }
источник
const findMeAnElementsFunkyArray = (arr, ele, i) => { const elementAtCurrentIndex = arr[i]; const differenceBetweenEleAndEleAtIndex = Math.abs( ele - elementAtCurrentIndex ); const hop = i + differenceBetweenEleAndEleAtIndex; if (i >= arr.length) { return; } if (arr[i] === ele) { return i; } const result = findMeAnElementsFunkyArray(arr, ele, hop); return result; }; const array = [4,5,6,5,4,3,2,3,4,5,6,7,8]; const answer = findMeAnElementsFunkyArray(array, 7, 0); console.log(answer);
Хотел включить рекурсивное решение проблемы. наслаждаться
источник