O(n)复杂度中子段中最大值的最小值

时间:2011-12-14 03:37:43

标签: arrays algorithm data-structures big-o

我几天前采访了亚马逊。我无法回答其中一个让我满意的问题。我在采访后试图得到答案,但到目前为止我还没有成功。这是一个问题:

你有一个大小为n的整数数组。您将获得参数k k < n。对于数组中大小为k的连续元素的每个段,您需要计算最大值。您只需要返回这些最大值的最小值。

例如,1 2 3 1 1 2 1 1 1k = 3,答案为1 细分受众群为1 2 32 3 13 1 11 1 21 2 12 1 11 1 1。 每个细分中的最大值为3332221。<登记/> 这些值中的最小值为1,因此答案为1

我想出的最佳答案是复杂度O(n log k)。我所做的是创建一个包含第一个k元素的二叉搜索树,在树中获取最大值并将其保存在变量minOfMax中,然后一次循环一个元素,其余元素在数组,从二叉搜索树中删除上一段中的第一个元素,在树中插入新段的最后一个元素,获取树中的最大元素,并将其与minOfMax进行比较,留在{{1这两个的最小值。

理想的答案需要具有复杂性O(n)。 谢谢。

3 个答案:

答案 0 :(得分:10)

有一种非常聪明的方法可以解决与 this earlier question 相关的问题。这个想法是可以构建一个queue data structure that supports enqueue, dequeue, and find-max in amortized O(1) time(有很多方法可以做到这一点;在原始问题中有两个解释)。获得此数据结构后,首先在O(k)时间将数组中的前k个元素添加到队列中。由于队列支持O(1)find-max,因此您可以在O(1)时间内找到这些k元素的最大值。然后,连续从队列中出队一个元素,并将下一个数组元素排入队列(在O(1)时间内)。然后,您可以在O(1)中查询每个k元素子阵列的最大值。如果您跟踪在数组过程中看到的这些值的最小值,那么您有一个O(n)时间,O(k)空间算法,用于查找k元素子阵列的最小最大值。

希望这有帮助!

答案 1 :(得分:9)

@ templatetypedef的答案有效,但我认为我有更直接的方法。

首先计算以下(关闭)间隔的最大值:

[k-1, k-1]
[k-2, k-1]
[k-3, k-1]
...
[0, k-1]

请注意,这些中的每一个都可以在前一个时间内以恒定时间计算。

接下来,计算这些间隔的最大值:

[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)时间生成表。结果如下。

答案 2 :(得分:0)

我在C#中实现(并评论过)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's out of its window k 
            }
            while (qi.Count>0 && arr[i] >= arr[qi.PeekBack()]) 
            {
                qi.PopBack();
            }
            qi.PushBack(i);
        }
        Console.WriteLine(arr[qi.PeekFront()]);
    }