滑动窗口求最大值超详细版,附deque模块介绍

滑动窗口求最大值超详细版 附deque模块介绍

deque是python的collections中的一个类

因此需要调用模块才能使用

from collections import deque

deque的对象像是一个列表,只不过可以固定这个deque对象的大小,以及列表是在队列的两端执行添加和弹出元素的操作,可以理解为,deque是一个双向的队列

以下是部分源码,以及部分方法:

class deque(object):
    """
    deque([iterable[, maxlen]]) --> deque object
	A list-like sequence optimized for data accesses near its endpoints.
	"""
    def append(self, *args, **kwargs): # real signature unknown
        """ Add an element to the right side of the deque. """
        pass

    def appendleft(self, *args, **kwargs): # real signature unknown
        """ Add an element to the left side of the deque. """
        pass

    def clear(self, *args, **kwargs): # real signature unknown
        """ Remove all elements from the deque. """
        pass

    def copy(self, *args, **kwargs): # real signature unknown
        """ Return a shallow copy of a deque. """
        pass

    def count(self, value): # real signature unknown; restored from __doc__
        """ D.count(value) -> integer -- return number of occurrences of value """
        return 0

    def extend(self, *args, **kwargs): # real signature unknown
        """ Extend the right side of the deque with elements from the iterable """
        pass

    def extendleft(self, *args, **kwargs): # real signature unknown
        """ Extend the left side of the deque with elements from the iterable """
        pass

    def index(self, value, start=None, stop=None): # real signature unknown; restored from __doc__
        """
        D.index(value, [start, [stop]]) -> integer -- return first index of value.
        Raises ValueError if the value is not present.
        """
        return 0

    def insert(self, index, p_object): # real signature unknown; restored from __doc__
        """ D.insert(index, object) -- insert object before index """
        pass

    def pop(self, *args, **kwargs): # real signature unknown
        """ Remove and return the rightmost element. """
        pass

    def popleft(self, *args, **kwargs): # real signature unknown
        """ Remove and return the leftmost element. """
        pass

    def remove(self, value): # real signature unknown; restored from __doc__
        """ D.remove(value) -- remove first occurrence of value. """
        pass

    def reverse(self): # real signature unknown; restored from __doc__
        """ D.reverse() -- reverse *IN PLACE* """
        pass

    def rotate(self, *args, **kwargs): # real signature unknown
        """ Rotate the deque n steps to the right (default n=1).  If n is negative, rotates left. """
        pass

deque的基本用法以及方法

创建deque的方法

from collections import deque
 
# 创建一个空的deque
data = deque()
print(data)	# deque([])

# 创建有数据的deque
data1 = deque('42你好。asdjnafs[]as\\136')
print(data1)	# deque(['4', '2', '你', '好', '。', 'a', 's', 'd', 'j', 'n', 'a', 'f', 's', '[', ']', 'a', 's', '\\', '1', '3', '6'])

# 创建有数据的deque
data2 = deque([1, 2, 3, 4])
print(data2)	# deque([1, 2, 3, 4])

创建deque时,并指定大小maxlen,即最多能装maxlen个元素, 以及deque添加元素append()方法

from collections import deque
 
# 创建一个空的deque, 并指定最大的元素是3个
data = deque(maxlen=3)
data.append(1)
data.append(2)
data.append(3)
print(data)	# deque([1, 2, 3], maxlen=3)
data.append(4)
print(data)	# deque([2, 3, 4], maxlen=3)

运行结果,我们可以看到,当新的元素加入并且这个队列已满的时候,最老的元素会自动被移除掉 因此新数据默认从右往左添加

appendleft()

appendleft就是王deque队列的左侧添加数据 append则是默认在尾部即右侧添加数据

data = deque('123')
print(data)
data.appendleft(0)
# deque([0, '1', '2', '3'])
clear()

清空deque队列,让其变成空队列

from collections import deque
 
    
data = deque('123')
print(data)	# deque(['1', '2', '3'])
data.clear()
print(data)	# deque([])
copy()

deque的copy方法相当于深拷贝,拷贝后的地址不相同,并且原来的值修改后,不会影响拷贝后的值

data1 = deque('123')
print(data1)  # deque(['1', '2', '3'])
data2 = data1.copy()
print(data2)  # deque(['1', '2', '3'])
print("data1的地址", id(data1))  # 2646053277264
print("data2的地址", id(data2))  # 2646053277504

data1.append('4')
print(id(data1))    # 2687303601744
print(id(data2))    # 2687303601744
count
data1 = deque('123333333')
count = data1.count('3')
print(count)	# 7
extend()

两个队列合并,extend(value), value的值可以是deque对象也可以是可迭代的对象,字符串,列表,元组等等

data1 = deque('123')
data2 = deque('236')
data1.extend(data2)
# deque(['1', '2', '3', '2', '3', '6'])
extendleft()

两个队列合并,从左侧合并,extendleft(value), value的值可以是deque对象也可以是可迭代的对象,字符串,列表,元组等等

要注意。合并时候,value的值,也是反着来的,注意看下面的打印,从左侧开始往里面加

data1 = deque('123')
data1.extendleft('456')
print(data1)
# deque(['6', '5', '4', '1', '2', '3'])
index方法

与字符串的find方法类似不赘述

data1 = deque('helloword')
print(data1.index('o'))     # 有多个的话,取第一个的索引位置
print(data1.index('o', 5))  # 从第五个开始(索引从0开始)
print(data1.index('o', 5, 8)) # 从第五个开始 -- 第八个结束
insert方法

与list的insert同理

from collections import deque
 
data1 = deque('helloword')
data1.insert(0, '1')    # 在第一个位置上插入 1
data1.insert(0, '2')    # 在第一个位置上插入 2
data1.insert(0, ['123'])    # 在第一个位置上插入 列表123
 
print(data1)
# deque([['123'], '2', '1', 'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'd'])

pop
from collections import deque
 
data1 = deque('12345')
print(data1.pop())  # 5 弹出元素,从右侧即末尾弹出,并返回
print(data1.pop())  # 4 弹出元素,从右侧即末尾弹出,并返回
print(data1)	# deque(['1','2','3']) 
reverse()
data1 = deque('12345')
data1.reverse()
print(data1)
# deque(['5', '4', '3', '2', '1'])

练习

接下来是一道来自力扣的困难题:

科技馆内有一台虚拟观景望远镜,它可以用来观测特定纬度地区的地形情况。该纬度的海拔数据记于数组 heights ,其中 heights[i] 表示对应位置的海拔高度。请找出并返回望远镜视野范围 limit 内,可以观测到的最高海拔值。

示例:

输入:heights = [14,2,27,-5,28,13,39], limit = 3
输出:[27,27,28,28,39]
解释:
  滑动窗口的位置                最大值
---------------               -----
[14 2 27] -5 28 13 39          27
14 [2 27 -5] 28 13 39          27
14 2 [27 -5 28] 13 39          28
14 2 27 [-5 28 13] 39          28
14 2 27 -5 [28 13 39]          39

这是笔者的原解题思路

def maxAltitude(heights, limit):
    """
    :type heights: List[int]
    :type limit: int
    :rtype: List[int]
    """
    true_list = []
    temp_list = []
    print(heights)
    if heights:
        if len(heights) <= limit:
            true_list.append(max(heights))
            return true_list

    for i in range(len(heights) - limit + 1):
        for j in range(limit):
            temp_list.append(heights[i + j])
        if temp_list:
            true_list.append(max(temp_list))
            temp_list = []
    return true_list


heights = [14, 2, 27]
limit = 3
list1 = maxAltitude(heights, limit)
print(list1)

可以看到非常的繁琐,不仅要嵌套for循环,还要专门为limit进行一次遍历

滑动窗口求最大值超详细版,附deque模块介绍_第1张图片

击败15.44%可以说是非常耻辱了,接下来看看利用deque模块的升级版

from collections import deque


def maxAltitude(heights, limit):
    data = deque()
    res = []

    # 前limit个数据中的最大值以及其deque
    for i in range(limit):
        while data and heights[i] >= heights[data[-1]]:
            data.pop()
        data.append(i)
    res.append(heights[data[0]])

    for j in range(limit, len(heights)):
        while data and heights[j] >= heights[data[-1]]:
            data.pop()
        data.append(j)
        while data[0] + limit <= j:
            data.popleft()
        res.append(heights[data[0]])

    return res


# 测试示例
heights = [1, 14, 2, 27, -5, 6, 3, 1, 28, 13, 39]
limit = 3
result = maxAltitude(heights, limit)
print(result)
# [14, 27, 27, 27, 6, 6, 28, 28, 39]

滑动窗口求最大值超详细版,附deque模块介绍_第2张图片

效率瞬间就上来了

接下来是解析,首先我们要搞懂双向队列在这道题中运用到的原理

【1】初始数据

# heights为数据 limit为窗口大小
heights = [1, 14, 2, 27, -5, 6, 3]
limit = 3

image-20231130200402786

【2】创建窗口

# 也就是代码中的
for i in range(limit):

滑动窗口求最大值超详细版,附deque模块介绍_第3张图片

【3】移动

第一步理所当然的 窗口没有数据也就是直接将’1’放进窗口,这对应这代码第二行的while data

heights[i]是整个数组的数据,data[i]是窗口中的数据,所以当data窗口为空时,无论下一个下标的数据是什么都直接data.appennd(i)

# [1, 14, 2, 27, -5, 6, 3]
for i in range(limit):<<<<<<<<i=0
        while data and heights[i] >= heights[data[-1]]:<<<<<<<<第一次判断 data不存在
            data.pop()
        data.append(i)<<<<<<<<此时代码走到了这里 heights[i]也就是1加入了窗口
res.append(heights[data[0]])

滑动窗口求最大值超详细版,附deque模块介绍_第4张图片

【4】继续移动

# [1, 14, 2, 27, -5, 6, 3]
for i in range(limit):<<<<<<<<i=1
        while data and heights[i] >= heights[data[-1]]:<<<<<<<<此时heights[i]=14明显大于data[-1]=1
            data.pop()<<<<<<<<此时1就被踢了出去
        data.append(i)<<<<<<<<然后放14进来
res.append(heights[data[0]]

滑动窗口求最大值超详细版,附deque模块介绍_第5张图片

【5】继续移动

# [1, 14, 2, 27, -5, 6, 3]
for i in range(limit):<<<<<<<<i=2
        while data and heights[i] >= heights[data[-1]]:<<<<<<<<此时heights[i]=2明显小于data[-1]=14
            data.pop()
        data.append(i)<<<<<<<<直接放入2 此时data窗口内容为[14, 2]
res.append(heights[data[0]]

滑动窗口求最大值超详细版,附deque模块介绍_第6张图片

【6】第一个窗口完毕

# [1, 14, 2, 27, -5, 6, 3]			
for i in range(limit):
        while data and heights[i] >= heights[data[-1]]:
            data.pop()
        data.append(i)
res.append(heights[data[0]]<<<<<<<<终于走完第一个窗口 将最大值14放进去

滑动窗口求最大值超详细版,附deque模块介绍_第7张图片

【7】踢掉2

# [1, 14, 2, 27, -5, 6, 3]			
for j in range(limit, len(heights)):# j=3
    while data and heights[j] >= heights[data[-1]]:<<<<<<<<227小
        data.pop()<<<<<<<<直接踢掉2
    data.append(j)
    while data[0] + limit <= j:
        data.popleft()
    res.append(heights[data[0]])

滑动窗口求最大值超详细版,附deque模块介绍_第8张图片

【8】踢掉14

# [1, 14, 2, 27, -5, 6, 3]			
for j in range(limit, len(heights)):# j=3
    while data and heights[j] >= heights[data[-1]]:<<<<<<<<14也比27小
        data.pop()<<<<<<<<直接踢掉14
    data.append(j)
    while data[0] + limit <= j:
        data.popleft()
    res.append(heights[data[0]])

滑动窗口求最大值超详细版,附deque模块介绍_第9张图片

【8】放27进来

# [1, 14, 2, 27, -5, 6, 3]			
for j in range(limit, len(heights)):# j=3
    while data and heights[j] >= heights[data[-1]]:
        data.pop()
    data.append(j)<<<<<<<<27进来
    while data[0] + limit <= j:
        data.popleft()
    res.append(heights[data[0]])

滑动窗口求最大值超详细版,附deque模块介绍_第10张图片

【9】比较data有没有超出长度

# [1, 14, 2, 27, -5, 6, 3]			
for j in range(limit, len(heights)):# j=3
    while data and heights[j] >= heights[data[-1]]:
        data.pop()
    data.append(j)
    while data[0] + limit <= j:<<<<<<<<data[0]也就是27,27的下标就是3, 3+3<=3不成立 放心往下走
        data.popleft()
    res.append(heights[data[0]])<<<<<<<<res = [14, 27]

滑动窗口求最大值超详细版,附deque模块介绍_第11张图片

【10】重复操作

# [1, 14, 2, 27, -5, 6, 3]			
for j in range(limit, len(heights)):# j=4
    while data and heights[j] >= heights[data[-1]]:<<<<<<<<27大于-5 等式不成立往下走
        data.pop()
    data.append(j)<<<<<<<<直接把-5放进来
    while data[0] + limit <= j:<<<<<<<<长度没超往下走
        data.popleft()
    res.append(heights[data[0]])<<<<<<<<res = [14, 27, 27]

滑动窗口求最大值超详细版,附deque模块介绍_第12张图片

【11】重复操作

# [1, 14, 2, 27, -5, 6, 3]			
for j in range(limit, len(heights)):# j=5
    while data and heights[j] >= heights[data[-1]]:<<<<<<<<27大于6 等式不成立往下走
        data.pop()
    data.append(j)<<<<<<<<直接踢掉56放进来
    while data[0] + limit <= j:<<<<<<<<长度没超往下走
        data.popleft()
    res.append(heights[data[0]])<<<<<<<<res = [14, 27, 27, 27]

滑动窗口求最大值超详细版,附deque模块介绍_第13张图片

如下:滑动窗口求最大值超详细版,附deque模块介绍_第14张图片

【12】窗口即将超出长度

# [1, 14, 2, 27, -5, 6, 3]			
for j in range(limit, len(heights)):# j=6
    while data and heights[j] >= heights[data[-1]]:<<<<<<<<27大于6 等式不成立往下走
        data.pop()
    data.append(j)<<<<<<<<直接把6放进来
    while data[0] + limit <= j:# 3+3=6 满足条件
        data.popleft()<<<<<<<<popleft把data左边的27踢了
    res.append(heights[data[0]])<<<<<<<<于是剩下了小6当老大 res=[14, 27, 27, 27, 6]

滑动窗口求最大值超详细版,附deque模块介绍_第15张图片

【13】结束

完结撒花

你可能感兴趣的:(Study,python,开发语言)