Минимальное значение максимальных значений в подсегментах... в 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()]);
    }