LeetCode 239. 滑动窗口最大值&二维滑动窗口

滑动窗口最大值

题目描述

给定一个数组和滑动窗口的大小,找出全部滑动窗口里数值的最大值。例如,若是输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有如下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。python

思路

用一个双端队列保存滑动窗口最大值,保证里面的元素顺序是从大到小的,若是当前入队元素小于队列前一个数值,就入队,由于一会窗口滑出可能这个小的就变成了最大值,
可是若是当前元素大于以前的元素,说明以前元素已经不是窗口内最大得了,就把前一个元素出队,一直到队空或者有元素大于当前元素,入队当前元素。这样队列里第一个元素永远都是最大的。
这里队列里保存的元素下标,而不是真正的数值,能够用下标来判断窗口大小。超出窗口大小就要滑出一部分数值。c++

code

c++:web

class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size)
    {
        deque<int>dq;
        vector<int>res;
        for(unsigned int i=0;i<num.size();++i){
            while((!dq.empty())&&num[dq.back()]<=num[i])
                dq.pop_back();
            while((!dq.empty())&&i-dq.front()+1>size)//不能够用dq.size>size,滑动窗口能够删掉了中间结点
                dq.pop_front();
            dq.push_back(i);
            if(i>=size-1)
                res.push_back(num[dq.front()]);
        }
        return res;
    }
};

python:数组

def maxInWindows(num, size):# 按照行求滑动窗口
	""" maxqueue用来保存当前最大值的【索引】,这里保存索引主要为了和当前位置对比,能够计算出是否超出窗口大小,超出就弹出; """
	maxqueue = []
	maxlist = []
	n = len(num)

	if n == 0 or size == 0 or size > n:
		return maxlist
	for i in range(n):
	    # 滑出窗口队首出队
		if len(maxqueue) > 0 and i - size >= maxqueue[0]: 
			maxqueue.pop(0)
		# 每超出窗口只要比队尾小就存进去,比队尾大就出队
		while len(maxqueue) > 0 and num[i] > num[maxqueue[-1]]:
			maxqueue.pop()
		maxqueue.append(i)
		if i >= size - 1:
			maxlist.append(num[maxqueue[0]])
	return maxlist

二维滑窗的最大值

题目描述

用一个二维滑动窗口在一个矩阵上滑动,找到每一个窗口的最大值,求这个矩阵或这些最大值的和
如:n =4,m=5,a=3, b=3,n和m为行列,a和b行列窗口大小,矩阵计算方式是 (i*j) mod 10, 矩阵为:[[1 2 3 4 5], [2 4 6 8 0], [3 6 9 2 5], [4 8 2 6 0]]
滑窗结果为:[[9, 9, 9],[9, 9, 9]],和为 54。app

思路

其实就是计算机视觉里的最大池化
1.直接用切片实现复杂度高
2.还用队列实现,先在行上作滑窗,用获得的结果在列上滑窗。svg

code

import numpy as np
n =4 
m =5
a =3
b =3

# [[0, 0, 0, 0, 0], 
# [0, 1, 2, 3, 4], 
# [0, 2, 4, 6, 8], 
# [0, 3, 6, 9, 2]]
# 4 8
# 6 9

nums = [[0 for j in range(m)] for i in range(n) ]
#print(nums)
for i in range(n):
	for j in range(m):
		nums[i][j] = (i+1)*(j+1)% 10  #从1开始

res = 0
nums = np.array(nums) # 转化成numpy矩阵才能多维度切片
#print(nums)
for i in range(0, n-a+1):
	for j in range(0, m-b+1):
		res += np.max(nums[i:i+a, j:j+b])

		#print('*'*10)
		#print(res)
#print(res.dtype)
print(res)
def maxInWindows(num, size):# 按照行求滑动窗口
	""" maxqueue用来保存当前最大值的【索引】,这里保存索引主要为了和当前位置对比 能够计算出是否超出窗口大小,超出就弹出; """
	maxqueue = []
	maxlist = []
	n = len(num)

	if n == 0 or size == 0 or size > n:
		return maxlist
	for i in range(n):
	    # 滑出窗口队首出队
		if len(maxqueue) > 0 and i - size >= maxqueue[0]: 
			maxqueue.pop(0)
		# 每超出窗口只要比队尾小就存进去,比队尾大就出队
		while len(maxqueue) > 0 and num[i] > num[maxqueue[-1]]:
			maxqueue.pop()
		maxqueue.append(i)
		if i >= size - 1:
			maxlist.append(num[maxqueue[0]])
	return maxlist

def maxInWindowsCol(num, size, n, m):#按照列求滑动窗口
	maxlist = []
	res = 0
	if n == 0 or size == 0 or size > n:
		return 0
	maxlist = [[] for i in range(n-size+1)]
	for i in range(m): #列
		maxqueue = []
		k = 0 # 列下标
		for j in range(n):	#行 
			if len(maxqueue) > 0 and j- size >= maxqueue[0]:
				maxqueue.pop(0)
			while len(maxqueue) > 0 and num[j][i] > num[maxqueue[-1]][i]:
				maxqueue.pop()
			maxqueue.append(j)
			if j >= size - 1:
				# maxlist.append(num[maxqueue[0]][i])
				res += num[maxqueue[0]][i]
				# print('k',k, 'num',num[maxqueue[0]][i])
				maxlist[k].append(num[maxqueue[0]][i])
				k += 1

	return maxlist, res
#print(maxInWindows([1,2,3,4,5],3))

def main():
	n =4 
	m =5
	a =3
	b =3
	nums = [[0 for j in range(m)] for i in range(n)]

	#print(nums)
	#初始化数组
	for i in range(n):
		for j in range(m):
			nums[i][j] = (i+1)*(j+1)% 10  #从1开始
	#print(nums)

	res = 0
	# 先按行求
	newnums=[]
	for i in range(n):
		newnums.append(maxInWindows(nums[i], a))
		#print(maxInWindows(nums[i],a))

	# 再用按行求获得结果按列求
	print(maxInWindowsCol(newnums, b, n, m-a+1)) # 行没变,列变成m-a+1

if __name__ == '__main__':
	main()