给定数组A和数字K创建数组B,其中B [i] = min(A [i],A [i + 1] .. A [i + K - 1])

时间:2014-11-02 15:08:35

标签: arrays algorithm deque

给定一个整数数组A和一个整数值K,创建数组B,其中B[i]是子数组A[i], A[i+1], ..., A[i+K-1]中的最小值1}}。请注意,B.length将等于A.length - K

例如,对于K = 3A=[1,2,3,4,0,1,2],解决方案为B=[1,2,0,0,0]

A  =  [1,2,3,4,0,1,2]
       _____| | | | |      
B[1] = 1      | | | | 
         _____| | | | 
B[2] =   2      | | | 
           _____| | | 
B[3] =         0  | | 
             _____| | 
B[4] =         0    | 
               _____| 
B[5] =         0

O(kn)时间复杂度的解决方案如下:

public static int[] createArray(int[] arr, int k) {
    int[] result = new int[arr.length];
    for (int i = 0; i <= arr.length - k; i++) {
        int curSmallestVal = arr[i];
        for (int j = i + 1; j < i + k; j++) {
            curSmallestVal = Math.min(curSmallestVal, arr[j]);
        }
        result[i] = curSmallestVal;
    }

    return result;
}

你能用O(n)运行时提供更优雅的解决方案吗? (可能使用队列)

使用O(n)解决方案进行更新:

public static int[] getMinSlidingWindow(int[] arr, int k) {
    int[] result = new int[arr.length-k+1];
    Deque<Integer> queue = new LinkedList<Integer>();
    //initialize sliding window
    for (int i = 0; i < k; i++) {
        if (!queue.isEmpty() && arr[queue.getLast()] >= arr[i])
            queue.removeLast();
        queue.addLast(i);
    }

    for (int i = k; i < arr.length; i++) {
        result[i-k] = arr[queue.getFirst()];
        while (!queue.isEmpty() && arr[queue.getLast()] >= arr[i])
            queue.removeLast();
        queue.addLast(i);
        while (!queue.isEmpty() && queue.getFirst() <= i-k)
            queue.removeFirst();
    }

    result[arr.length-k] = arr[queue.removeFirst()]; 

    return result;
}

3 个答案:

答案 0 :(得分:2)

使用具有双端队列的标准滑动窗口最小算法可以实现O(n)时间复杂度。以下是详细说明:http://people.cs.uct.ac.za/~ksmith/articles/sliding_window_minimum.html

答案 1 :(得分:1)

我想到的第一个想法是使用两套。两个集都存储std::pair<index_t,value_t>但具有不同的顺序。 (一个按索引排序,一个按值排序)。通过这种方式,您可以在每个步骤中迭代通过数组查找最小值(按值排序的集合中的第一个元素)以及从两个集合中删除的元素/对(按索引排序的集合中的第一个元素)。在每个步骤中,您在每个集合中添加对,并从每个集合中删除对。

答案 2 :(得分:1)

使用双端队列(支持从前端和后端添加推送和弹出的队列)和一些额外的逻辑,您可以构建一个运行O(n)的解决方案。

这是解决方案的伪代码。

void getMaxSlidingWindow(int[] A, int k) {
    int[] B = new int[A.length - k];
    // Store the indexes of A in Q
    // Q.front(): index of smallest element in the window, Q.back(): index of the largest one in the window
    DobuleEndedQueue<int> Q = new DobuleEndedQueue<int>(); 

    for(int i=0; i<k; i++) {
       // Fill up the double ended queue for the first k elements
       // Remove elements that we would ignore because they're bigger than the next one in the window
       while(!Q.empty() && A[i] <= A[Q.back()]) {
          Q.popBack();
       }
       Q.pushBack(i);
    }

     for(int i=k; i < A.length; i++) {
       B[i - k] = A[Q.front()]; // The first element in the queue is the index of the smallest element in the window
       // Add the current element to the queue. Before we do, remove all elements that we would ignore immediately because they're bigger than the current one
       while(!Q.empty() && A[i] <= A[Q.back()]  ) {
          Q.popBack();
       }
       Q.pushToBack(i);
       // Remove any index from the front of the queue which is no longer in the window
       while(!Q.empty() && Q.front() <= i-k) {
         Q.popFront();
       }
    }
    B[A.length - k] = A[Q.front()];
}

此解决方案的时间复杂度是O(n):我们遍历所有元素一次,然后将它们添加或移除到双端队列一次。完成的最大操作是2n,这是O(n)复杂度。

要使此解决方案正常工作,您需要使用以下操作实现双端队列数据结构:

class DobuleEndedQueue
int front(), void pushFront(int n), void popFront() // peeks at the front element, adds and removes to the front
int back(), void pushBack(int n) , void popBack() // same for the back element

通过一个简单的例子进一步解释算法:

  • 遍历前k个元素并将这些作为索引插入到双端Q数据结构中,以便A [Q.front()]是最小元素,A [Q.back()]是最大元素。窗口。
  • 当我们构建窗口时,从这个队列中抛出“不必要的”元素:既不会计数的最小元素也不会在窗口之外的元素
  • 为A = [8,6,9,2]和k-3
  • 构建队列的示例
    1. Q = [0],(将0插入Q的背面,因为A [0]是我们见过的最小元素。)
    1. Q = [1],(A [1]&lt; Q.back()因此弹出后面元素并将其替换为1.我们这样做是因为A [0]在寻找最小数字时无关紧要现在。)
    1. Q = [1,2],B = [8](A [2]是&gt; Q.back(),所以我们只在Q上加2.B [0]将是最小的项目Q,即A [Q.first()],即A [1])
    1. Q = [3],B = [8,2](A [4]小于Q中的所有元素,所以我们弹出所有元素.B [1]将是Q中的最小项,A [ Q.first()],即A [3]