Finding the max of each continguous subarray of a given size

醉酒当歌 提交于 2019-12-25 04:29:35

问题


I'm trying to solve the following problem in Python

Given an array and an integer k, find the maximum for each and every contiguous subarray of size k.

The idea is to use a double ended queue. This is my code:

def diff_sliding_window(arr, win):
#     max = -inf
    Q = []
    win_maxes = [] # max of each window
    for i in range(win):
        print(Q)
        while len(Q) > 0 and arr[i] >= arr[len(Q) - 1]:
            # get rid of the index of the smaller element
            Q.pop() # removes last element
        Q.append(i)
#     print('>>', Q)

    for i in range(win, len(arr)):
#         win_maxes.append(arr[Q[0]])
        print(arr[Q[0]])
        while len(Q) > 0 and Q[0] <= i - win:
            Q.pop()
        while len(Q) > 0 and arr[i] >= arr[len(Q)-1]:
            Q.pop(0)
        Q.append(i)
#     win_maxes.append(arr[Q[0]])

    print(arr[Q[0]])

But I can't figure out why for the test cases:

t1 = [1, 3, -1, -3, 5, 3, 6, 7]
t2 = [12, 1, 78, 90, 57, 89, 56]

that I'm not getting the correct results.


Update:

I've made the changes that Matt Timmermans suggested, but I'm still not obtaining the proper output. For t2, and win = 3

78
90
90
89 <--- should be 90
89

Here is my updated code:

from collections import deque

def diff_sliding_window(arr, win):
#     max = -inf
    Q = deque()
    win_maxes = [] # max of each window
    for i in range(win):
#         print(Q)
        while len(Q) > 0 and arr[i] >= arr[Q[len(Q)-1]]:
            # get rid of the index of the smaller element
            Q.pop() # removes last element
        Q.append(i)
#     print('>>', Q)

    for i in range(win, len(arr)):
#         win_maxes.append(arr[Q[0]])
        print(arr[Q[0]])
        while len(Q) > 0 and Q[0] <= i - win:
            Q.pop()
        while len(Q) > 0 and arr[i] >= arr[Q[len(Q)-1]]:
            Q.popleft()
        Q.append(i)


    print(arr[Q[0]])

回答1:


It looks like you are trying to implement the O(n) algorithm for this problem, which would be better than the other two answers here at this time.

But, your implementation is incorrect. Where you say arr[i] >= arr[len(Q)-1], you should say arr[i] >= arr[Q[len(Q)-1]] or arr[i] >= arr[Q[-1]]. You also swapped the pop and pop(0) cases in the second loop. It looks like it will be correct after you fix those.

Also, though, your algorithm is not O(n), because you using Q.pop(0), which takes O(k) time. Your total running time is therefore O(kn) instead. Using a deque for Q will fix this.

Here it is all fixed, with some comments to show how it works:

from collections import deque

def diff_sliding_window(arr, win):

    if win > len(arr):
        return []

    win_maxes = [] # max of each window

    #Q contains indexes of items in the window that are greater than
    #all items to the right of them.  This always includes the last item
    #in the window
    Q = deque()

    #fill Q for initial window
    for i in range(win):
        #remove anything that isn't greater than the new item
        while len(Q) > 0 and arr[i] >= arr[Q[-1]]:
            Q.pop()
        Q.append(i)

    win_maxes.append(arr[Q[0]])

    for i in range(win, len(arr)):
        #remove indexes (at most 1, really) left of window
        while len(Q) > 0 and Q[0] <= (i-win):
            Q.popleft()

        #remove anything that isn't greater than the new item
        while len(Q) > 0 and arr[i] >= arr[Q[-1]]:
            Q.pop()
        Q.append(i)
        win_maxes.append(arr[Q[0]])

    return win_maxes

try it: https://ideone.com/kQ1qsQ

Proof that this is O(N): Each iteration of the inner loops removes an item from Q. Since there are only len(arr) added to Q in total, there can be at most len(arr) total iterations of the inner loops.




回答2:


What about this approach (which only needs one pass over the data):

Code

def calc(xs, k):
    k_max = []
    result = []

    for ind, val in enumerate(xs):
        # update local maxes (all are active)
        for i in range(len(k_max)):
            if val > k_max[i] :
                k_max[i] = val
        # one new sub-array starts
        k_max.append(val)

        if ind >= (k-1):  # one sub-array ends
            result.append(k_max[0])
            k_max.pop(0)

    return result

t1 = [1, 3, -1, -3, 5, 3, 6, 7]
t2 = [12, 1, 78, 90, 57, 89, 56]
print(calc(t1, 3))
print(calc(t2, 2))

Output

[3, 3, 5, 5, 6, 7]
[12, 78, 90, 90, 89, 89]



回答3:


Here's a simple solution using itertools and tee:

def nwise(iterable, n):
    ''' Step through the iterable in groups of n '''
    ts = it.tee(iterable, n)
    for c, t in enumerate(ts):
        next(it.islice(t, c, c), None)
    return zip(*ts)

def max_slide(ns, l):
    return [max(a) for a in nwise(ns, l)]

>>> max_slide([1, 3, -1, -3, 5, 3, 6, 7], 3)
[3, 3, 5, 5, 6, 7]
>>> max_slide([12, 1, 78, 90, 57, 89, 56], 3)
[78, 90, 90, 90, 89]


来源:https://stackoverflow.com/questions/39885520/finding-the-max-of-each-continguous-subarray-of-a-given-size

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!