之前博客写过一道关于大顶堆小顶堆的优先队列,Python 返回数据流中的第k大元素 LeetCode NO.703。这题也可以用这种方法很好的求解。
思路:
- 我们可以来维护k大小的大顶堆。这个堆顶的元素就是最大值。可以想到我们对堆进行就可以得最后的结果。举个栗子?,维护一个【1,3,-1】这样的大顶堆,堆顶3,然后先删除最左边的元素,然后加入-3,调整大顶堆的元素。复杂度很容算出来就是O(N log k)
# 思路1
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
import heapq
if not nums: return []
window,result = list(),list()
for i,x in enumerate(nums):
window.append(x)
if len(window) == k:
result.append(heapq.nlargest(1,window)[0])
window = window[1:]
return result
- 那么如何把复杂度降低到线性的呢。由于k的值是给定的,也就是程序运行开始,到结束k不变。那么我们可以来维护这样一个队列,左边出右边进。并且当进来的元素的时候,我们需要让最左边的元素始终为最大的那个。举个栗子?,一开始为【1】添加,【1,3】,维护,【3】,添加【3,-1】,维护,不发生变化,添加【3,-1,-3】,维护不发生变化,添加【-1,-3,5】,维护【5】。通过代码可以,维护一个window的时候,可以大致认为为平均下来为O(1),那么整体就是一个线性复杂度。
#思路2
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
if not nums: return []
window, result = list(),list()
for i,x in enumerate(nums):
#使得window窗口一直为k,注意window里面存的是index
if i >= k and window[0] <= i-k:
window.pop(0)
#维护队列,使得最左边的元素为最大值
while window and nums[window[-1]] <= x:
window.pop()
window.append(i)
if i >= k-1:
result.append(nums[window[0]])
return result
- 其实看到题目的时候,也会想到使用动态规划,但是怎写是个问题。以下是LeetCode的解析,提供一种思路。
#思路3,太巧妙了,除非面试出现原题,反正我是想不出来
class Solution:
def maxSlidingWindow(self, nums: 'List[int]', k: 'int') -> 'List[int]':
n = len(nums)
if n * k == 0:
return []
if k == 1:
return nums
left = [0] * n
left[0] = nums[0]
right = [0] * n
right[n - 1] = nums[n - 1]
for i in range(1, n):
# from left to right
if i % k == 0:
# block start
left[i] = nums[i]
else:
left[i] = max(left[i - 1], nums[i])
# from right to left
j = n - i - 1
if (j + 1) % k == 0:
# block end
right[j] = nums[j]
else:
right[j] = max(right[j + 1], nums[j])
output = []
for i in range(n - k + 1):
output.append(max(left[i + k - 1], right[i]))
return output