Минимальное значение максимальных значений в подсегментах... в O (n) сложности
Я беседовал с Amazon несколько дней назад. Я не мог ответить на один из вопросов, которые меня попросили удовлетворить. Я попытался получить ответ после интервью, но до сих пор я не был успешным. Вот вопрос:
У вас есть массив целых чисел размера n. Вам предоставляется параметр k
где k < n
. Для каждого сегмента последовательных элементов размера k
в массиве вам нужно вычислить максимальное значение. Вам нужно только вернуть минимальное значение этих максимальных значений.
Например, если указано 1 2 3 1 1 2 1 1 1
и k = 3
, ответ будет 1
.
Сегменты будут 1 2 3
, 2 3 1
, 3 1 1
, 1 1 2
, 1 2 1
, 2 1 1
, 1 1 1
.
Максимальные значения в каждом сегменте: 3
, 3
, 3
, 2
, 2
, 2
, 1
.
Минимум этих значений 1
, поэтому ответ 1
.
Лучший ответ, который я придумал, - это сложность O (n log k). Я создаю двоичное дерево поиска с помощью первых элементов k
, получаю максимальное значение в дереве и сохраняю его в переменной minOfMax
, затем циклически объединяем один элемент за один раз с оставшимися элементами в массиве, удаляем первый элемент предыдущего сегмента из дерева двоичного поиска, вставьте последний элемент нового сегмента в дерево, получите максимальный элемент в дереве и сравните его с minOfMax
, оставив в minOfMax
минимальное значение двух.
Идеальный ответ должен быть сложным O (n).
Спасибо.
Ответы
Ответ 1
Существует очень умный способ сделать это, связанный с этим более ранним вопросом. Идея состоит в том, что можно построить структуру данных очереди которая поддерживает привязку, dequeue и find-max в амортизированном O (1) времени (есть много способов сделать это, два объясняются в исходном вопросе). Когда у вас есть эта структура данных, начните с добавления первых элементов k из массива в очередь в O (k) времени. Поскольку очередь поддерживает O (1) find-max, вы можете найти максимум этих k элементов в O (1) раз. Затем, непрерывно деактивируйте элемент из очереди и enqueue (в O (1) раз) следующий элемент массива. Затем вы можете запросить в O (1), что такое максимум каждого из этих подмассивов k-элементов. Если вы отслеживаете минимум этих значений, которые вы видите в ходе массива, тогда у вас есть O (n) -time, O (k) -пространственный алгоритм для нахождения минимального максимума подмассивов k-элементов.
Надеюсь, это поможет!
Ответ 2
@templatetypedef отвечает, но я думаю, что у меня есть более прямой подход.
Начните с вычисления max для следующих (закрытых) интервалов:
[k-1, k-1]
[k-2, k-1]
[k-3, k-1]
...
[0, k-1]
Обратите внимание, что каждый из них может быть вычислен в постоянное время от предыдущего.
Затем вычислите max для этих интервалов:
[k, k]
[k, k+1]
[k, k+2]
...
[k, 2k-1]
Теперь эти интервалы:
[2k-1, 2k-1]
[2k-2, 2k-1]
[2k-3, 2k-1]
...
[k+1, 2k-1]
Затем вы делаете интервалы от 2k до 3k-1 ( "передовые интервалы" ), затем от 3k-1 до 2k + 1 ( "обратные интервалы" ). И так далее, пока вы не достигнете конца массива.
Поместите все это в большую таблицу. Обратите внимание, что каждая запись в этой таблице занимает постоянное время для вычисления. Обратите внимание, что в таблице не более 2 * n интервалов (поскольку каждый элемент появляется один раз в правой части "передового интервала" и один раз на левой стороне "обратного интервала" ).
Теперь, если [a, b] - любой интервал ширины k, он должен содержать ровно один из 0, k, 2k,...
Скажем, он содержит m * k.
Заметим, что интервалы [a, m * k-1] и [m * k, b] находятся где-то в нашей таблице. Таким образом, мы можем просто просмотреть максимум для каждого, а максимальный из этих двух значений - это максимальный интервал [a, b].
Итак, для любого интервала ширины k мы можем использовать нашу таблицу, чтобы получить ее максимум в постоянное время. Мы можем генерировать таблицу в O (n) времени. Результат следует.
Ответ 3
Я реализовал (и прокомментировал) ответ templatetypedef в С#.
n
- длина массива, k
- размер окна.
public static void printKMax(int[] arr, int n, int k)
{
Deque<int> qi = new Deque<int>();
int i;
for (i=0 ; i < k ; i++) // The first window of the array
{
while ((qi.Count > 0) && (arr[i] >= arr[qi.PeekBack()]))
{
qi.PopBack();
}
qi.PushBack(i);
}
for(i=k ; i < n ; ++i)
{
Console.WriteLine(arr[qi.PeekFront()]); // the first item is the largest element in previous window. The second item is its index.
while (qi.Count > 0 && qi.PeekFront() <= i - k)
{
qi.PopFront(); //When it out of its window k
}
while (qi.Count>0 && arr[i] >= arr[qi.PeekBack()])
{
qi.PopBack();
}
qi.PushBack(i);
}
Console.WriteLine(arr[qi.PeekFront()]);
}