На практическом собеседовании запрашивается максимальное значение ji при условии, что array[j] >= array[i]; не понимаю решение
Я читаю эту статью здесь: https://www.geeksforgeeks.org/given-an-array-arr-find-the-maximum-j-i-such-that-arrj-arri/ и не могу понять, как решение O(n) работает. Параграф, описывающий это, кажется, противоречит кодексу. Я просмотрел образец массива и вручную убедился, что это работает, но мне это не кажется интуитивно понятным.
Будет ли кто-то более опытный в решении головоломок программирования хотеть объяснить, как и почему это работает, или объяснить, что с ним не так?
Спасибо.
(Ниже текст по ссылке выше:)
Проблема:
По массиву arr [] найдите максимум j - i, такой что arr[j] > arr[i]. По массиву arr [] найдите максимум j - i такой, что arr[j] > arr[i].
Примеры:
Input: {34, 8, 10, 3, 2, 80, 30, 33, 1}
Output: 6 (j = 7, i = 1)
Input: {9, 2, 3, 4, 5, 6, 7, 8, 18, 0}
Output: 8 ( j = 8, i = 0)
Метод 2 (Эффективный)
Чтобы решить эту проблему, нам нужно получить два оптимальных индекса arr[]: левый индекс i и правый индекс j. Для элемента arr [i] нам не нужно рассматривать arr [i] для левого индекса, если в левой части arr [i] есть элемент меньше, чем arr [i]. Аналогично, если в правой части arr[j] есть больший элемент, нам не нужно рассматривать этот j для правильного индекса. Таким образом, мы строим два вспомогательных массива LMin[] и RMax[] так, чтобы LMin[i] содержал наименьший элемент в левой части arr[i], включая arr[i], а RMax[j] содержал наибольший элемент в правой части arr[j], включая arr[j]. После построения этих двух вспомогательных массивов мы пересекаем оба этих массива слева направо. Обходя LMin[] и RMa[], если мы видим, что LMin[i] больше чем RMax[j], то мы должны двигаться вперед в LMin[] (или сделать i++), потому что все элементы слева от LMin[i] больше или равно LMin[i]. В противном случае мы должны двигаться вперед в RMax[j], чтобы найти большее значение j - i.
2 ответа
Оно работает. Автор кода просто взял запутанный ярлык.
Как указывает редакция, данные показатели i1 < i2
с arr[i1] ≤ arr[i2]
нет смысла рассматривать i = i2
, Мы всегда можем добиться большего успеха, установив i = i1
вместо этого, так как для всех j
,
j - i1 > j - i2
, а также- если
arr[j] > arr[i2]
, то тот факт, чтоarr[i2] ≥ arr[i1]
подразумевает, чтоarr[j] > arr[i1]
,
Точно так же, учитывая индексы j1 < j2
с arr[j1] ≤ arr[j2]
нет смысла рассматривать j = j1
,
Давайте рассмотрим первый пример ввода и исключим всех этих неоптимальных кандидатов.
34 8 10 3 2 80 30 33 1
34 8 3 2 1 // candidate values of arr[i]
80 33 1 // candidate values of arr[j]
Заметьте, что обе подпоследовательности уменьшаются. Это прямое следствие вышеуказанных условий для того, чтобы быть кандидатом.
В поисках лучшего i
а также j
теперь включает в себя соревнование по программированию клише. Позвольте мне организовать возможности в таблице. Обратите внимание, что алгоритм не создает эту таблицу явно; это просто наглядное пособие.
80 33 1
-------------
34 √
8 √ √
3 √ √
2 √ √
1 √ √
Галочка (√
) Значит это arr[i] < arr[j]
, Понижение означает увеличение i
и уменьшается arr[i]
, Движение влево означает уменьшение j
и увеличивается arr[j]
, Следовательно, везде, где есть галочка, все квадраты ниже или слева или какая-то комбинация также имеют галочки. С другой стороны, учитывая квадрат с галочкой, который находится ниже / слева от другого квадрата с галочкой, последний квадрат обязательно является лучшим решением, потому что i
меньше или j
больше или оба. Поэтому нас очень интересует граница между галочками и отсутствием галочек, потому что именно в этом заключается оптимальное решение.
Алгоритм отслеживания границы прост. Начните с верхнего левого квадрата. Если у текущего квадрата есть галочка, идите направо. Иначе иди вниз. Я надеюсь, что вы сможете понять, как этот процесс посещает первую галочку в каждом столбце во времени O(#rows + #columns)
, Вот еще один возможный стол.
33 8 7 3
---------------
34
8 √
3 √ √ √
2 √ √ √ √
1 √ √ √ √
Чтобы перейти к реализации, обратите внимание, что мы выполняем тот же поиск, но с некоторыми дублированными строками / столбцами, чтобы заполнить пустое пространство, оставленное не кандидатами, что избавляет нас от необходимости следить за соответствием между индексами. Это в основном та же идея.
Это магия монотонных функций и то понимание, которое можно получить, визуализируя пространство решения проблемы и то, как эти значения выравниваются. Давайте нарисуем один из примеров на странице, на которую вы ссылаетесь; индексы массива по оси x, LMin и RMax по оси y:
{34, 8,10, 3, 2,80,30,33, 1}
0 1 2 3 4 5 6 7 8
80 r r r r r r
.
34 l
33 r r
. ^
30
.
10
.
8 l l
. ^
3 l
2 l l l l
1 lr
0 1 2 3 4 5 6 7 8
r
значение не является (обязательно) значением, связанным с индексом массива; скорее это показатель того, как далеко мы можем продлить интервал вправо, гарантируя отсутствие r
справа будет больше (имеется в виду, что мы могли бы пропустить больший интервал). Точно так же l
является показателем того, что мы находимся на самом низком левом значении, и так как мы впервые исследуем движение вдоль r
s, мы гарантированно не пропустили ранее l
за любой интервал в нашем поиске. Ясно, что мы перебираем слева направо, самое большее, по всем r
и все l
с, так O(n)
,