数组中最后k个值的最高值,优于O(n ^ 2)

时间:2014-01-20 01:31:01

标签: arrays algorithm

我有一个n个元素的数组,对于每个元素,我试图找到最后k个值中的最高值(包括当前值)?例如。给出k,

int[] highestValues = new int[arr.Length];
for (int i = k - 1; i < arr.Length; i++)
{
    int highest = Int32.MinValue;
    for (int j = i - k; j <= i; j++)
    {
        highest = Math.Max(highest, arr[j]);
    }
    highestValues[i] = highest;
}

该算法的复杂性似乎是O(n ^ 2)。我想知道是否有任何方法可以更快地完成,例如而不是比较内部循环中的k个数字,重用上一个操作的结果。

3 个答案:

答案 0 :(得分:2)

这个问题有一个很好的解决方案,使用 Dequeue 数据结构(在第一个和最后一个位置支持添加和检查元素的结构)。

我们创建类Entry来存储元素的索引和该元素的值。观察时间复杂度约为2 * n = O(2 * n)= O(n)(每个元素只添加一次到队列中,我们也遍历队列中的每个元素一次:在队列的第一个或最后一个位置删除它。

伪代码:

class Entry {
    int value, index;
}

int [] highestValues;

Dequeue queue = new Dequeue();   

 for(int i = 0; i < arr.Length; i++){
        Entry en = new Entry(arr[i], i);
        while(queue.last().value <= en.value){//Remove all elements smaller than the current element.
            queue.removeLast();
        }
        queue.append(en);
        if(i >= k){
            highestValues[i] = queue.first().value;
            while(queue.first().index < i - k){//Remove all elements which has index out of the range
                 queue.removeFirst();
            }
        }
    }    
}

答案 1 :(得分:1)

这是最大滑动窗口问题滑动窗口中的最大值数组

请参阅:

关键是双端队列允许从开始和结束中删除元素,以便在我们滑动时保持最大值。

来源示例:

import java.util.ArrayDeque;
import java.util.Deque;

公共类SlidingWindow {

public static void maxSlidingWindow(int A[], int n, int w, int B[]) {
    Deque<Integer> Q = new ArrayDeque<Integer>();

    // Initialize deque Q for first window
    for (int i = 0; i < w; i++) {
        while (!Q.isEmpty() && A[i] >= A[Q.getLast()])
            Q.pollLast();
        Q.offerLast(i);
    }

    for (int i = w; i < n; i++) {
        B[i - w] = A[Q.getFirst()];

        // update Q for new window
        while (!Q.isEmpty() && A[i] >= A[Q.getLast()])
            Q.pollLast();

        // Pop older element outside window from Q
        while (!Q.isEmpty() && Q.getFirst() <= i - w)
            Q.pollFirst();

        // Insert current element in Q
        Q.offerLast(i);
    }
    B[n - w] = A[Q.getFirst()];
}

public static void main(String args[]) {
    int k = 20;

    int a[] = new int[100];
    for(int i=0; i<100; i++)
    a[i] = i;

    int b[] = new int[a.length - k + 1];

    maxSlidingWindow(a, a.length, k, b);

    System.out.println("Sliding Window Maximum is ");
    for (int i = 0; i < b.length; i++) {
        System.out.print(b[i] + ",");
    }

}

}

答案 2 :(得分:0)

我有一个我认为有效的实现(用C#编码):

public static int[] LastKHighestValues(int[] array, int k)
{
    int[] maxes = new int[array.Length];

    int indexOfCurrentMax = 0;
    int indexOfNextMax = 0;

    for (int i = 0; i < array.Length; i++)
    {
        if (indexOfNextMax <= indexOfCurrentMax ||
            array[i] > array[indexOfNextMax])
        {
            indexOfNextMax = i;
        }
        if (i - indexOfCurrentMax >= k)
        {
            indexOfCurrentMax = indexOfNextMax;
        }

        if (array[i] > array[indexOfCurrentMax])
        {
            indexOfCurrentMax = i;
        }


        maxes[i] = array[indexOfCurrentMax];
    }

    return maxes;
}

这个想法是你保留两个“指针”:一个指向当前最大值,一个指向当前最大值到期后的下一个指针。这可以在一次通过数组中实现(所以O(n))。

我有一些通过测试,但我很难确定我已经涵盖了每个角落的情况:

Puzzle.LastKHighestValues(new[] {4, 3, 1}, 1).Should().Equal(new[] {4, 3, 1});
Puzzle.LastKHighestValues(new[] { 7, 7, 7 }, 3).Should().Equal(new[] { 7, 7, 7 });
Puzzle.LastKHighestValues(new[] { 7, 7, 7 }, 4).Should().Equal(new[] { 7, 7, 7 });
Puzzle.LastKHighestValues(new[] { 3, 2, 1 }, 2).Should().Equal(new[] { 3, 3, 2 });
Puzzle.LastKHighestValues(new[] { 7, 1, 4, 1, 1 }, 3).Should().Equal(new[] { 7, 7, 7, 4, 4 });
Puzzle.LastKHighestValues(new[] { 7, 8, 4, 9, 10 }, 2).Should().Equal(new[] { 7, 8, 8, 9, 10 });