01-单调栈和窗口及其更新结构

题一:​​​​​​​ 

题目:  给定一个数组 list 和一个数字 w ,要求获取 list 中滑动窗口为 w 的最大值分析:  构建一个双端队列获取最大值,从头部到尾部保证从大到小的顺序,则可获得当前窗  口的最大值(队列头部第一个数),往双端队列里面加数的逻辑:  当前数和双端队列的尾部的数进行比较,  while 当前数 > 双端队列的尾部的数 or len(双端队列) > 0 :    则将尾部的数进行弹出  双端队列.append(当前数)    这么做为什么会有效呢?因为这样构建出来的双端队列头部的数字有如下两个特性:    1.数值是最大的    2.比弹出的数晚过期(下标index大)

代码实现:

 def get_max_value_from_window(self,arr:list,win_len:int):
        q_max = deque()
        print(f"arr:{arr}")
        print(f"win_len:{win_len}")
        if len(arr) < win_len :
            return None
        res = []
        for i in range(0,len(arr)):
            while q_max.__len__() > 0 and arr[q_max[-1]] < arr[i] :
                q_max.pop()
            q_max.append(i)
        #
            if i >= win_len-1 and q_max[0] <= i - win_len:
                # 剔除窗口外失效的值
                q_max.popleft()
            if i >= win_len-1:
                res.append(arr[q_max[0]])
        return res    

结果输出:

输入arr:[2, 4, 5, 5, 4, 3, 8, 55, 6, 3, 9, 1]
输入win_len:4
输出:[5, 5, 5, 8, 55, 55, 55, 55, 9]

题二:​​​​​​​

题目:    给定一个整型数组arr,和一个整数num,某个arr中的子数组sub,如果想达标,    必须满足:    sub中最大值 - sub中最小值 <= num ,    返回arr中达标子数组的数量分析:    sub = arr[L:R]  若sub是满足上述条件的数组,    当L和R往里缩时,形成新的数组都满足上述条件,    当L和R往外扩时,形成新的数组都不满足上述条件

代码实现:

 def get_sub_arr_num(self,arr,num):
        print(title)
        print(f"输入 arr:{arr},num:{num}")
        res = 0
        L = 0
        R = 0
        q_max = deque()
        q_min = deque()
        while L < arr.__len__():

            while R < arr.__len__():
                while q_max.__len__() != 0 and arr[q_max[-1]] <= arr[R]:
                    # 如果 q_max 不为空,并且q_max中最后一个的值 < arr[i] ,则进行右弹出
                    q_max.pop()
                #     弹出完之后 ,将新数据加入 q_max
                q_max.append(R)

                while q_min.__len__() != 0 and arr[q_min[-1]] >= arr[R]:
                    # 如果 q_min 不为空,并且 q_min 中最后一个的值 < arr[i] ,则进行右弹出
                    q_min.pop()
                #     弹出完之后 ,将新数据加入 q_min
                q_min.append(R)

                if q_max.__len__() > 0 and q_max[0] < L :
                    q_max.popleft()
                if q_min.__len__() > 0 and q_min[0] < L :
                    q_min.popleft()

                if arr[q_max[0]] - arr[q_min[0]] > num:
                    cnt = len(arr[L:R]) - 1
                    res += cnt
                    print(f"R:{R},L:{L},R-L:{R-L}\t",arr[L:R],cnt)
                    break
                R += 1
            L += 1
        return res

结果输出:

输入 arr:[2, 4, 5, 5, 4, 3, 8, 55, 6, 3, 9, 1],num:4
输出:16

题三:​​​​​​​

获取数组中的每个值,左边离它最近且值比它小的下标,没有则为-1右边离它最近且值比它小的下标,没有则为-1

代码实现:

def get_closed_min(self,arr):
        """获取数组中的每个值,左边离它最近且值比它小的下标,没有则为-1
            右边离它最近且值比它小的下标,没有则为-1
        """
        print("输入:",arr)
        res = []
        que = deque()  # 栈内维持由小到大的单调性
        if len(arr) == len(set(arr)):
            "说明数组中没有重复值"
            for i in range(0,len(arr)):
                if que.__len__() != 0 :
                    if que.__len__() > 0 and que[-1] > arr[i] :
                        while que.__len__() and que[-1] > arr[i]:
                            # 说明 arr[i] 是 que[-1] 右边比它小的值中离它最近的值
                            r_min = arr[i]
                            cur = que.pop()
                            if que.__len__() > 0 :
                                # 弹出后,如果栈中还有值,说明栈弹出的第一个值是 cur 左边比它小的值中离它最近的值
                                l_min = que[-1]
                            else:
                                l_min = -1
                            res.append([cur,l_min,r_min])
                que.append(arr[i])
            while que.__len__() != 0:
                cur = que.pop()
                r_min = -1
                if que.__len__() > 0:
                    # 弹出后,如果栈中还有值,说明栈弹出的第一个值是 cur 左边比它小的值中离它最近的值
                    l_min = que[-1]
                else:
                    l_min = -1
                res.append([cur,l_min,r_min])
            return res
        else:
            "说明数组中有重复值"
            for i in range(0,len(arr)):
                if que.__len__() != 0 :
                    if que.__len__() > 0 and arr[que[-1][-1]] > arr[i] :
                        while que.__len__() and arr[que[-1][-1]] > arr[i]:
                            # 说明 arr[i] 是 que[-1] 右边比它小的值中离它最近的值
                            r_min = i
                            curs = que.pop()
                            for cur in curs:
                                if que.__len__() > 0 :
                                    # 弹出后,如果栈中还有值,说明栈弹出的第一个值是 cur 左边比它小的值中离它最近的值
                                    l_min = que[-1][-1]
                                else:
                                    l_min = -1
                                res.append([cur,l_min,r_min])
                    else:
                        if arr[que[-1][-1]] == arr[i]:
                            tmp = que.pop()
                            tmp.append(i)
                            que.append(tmp)
                            continue
                if que.__len__() > 0 and arr[que[-1][-1]] == arr[i]:
                    tmp = que.pop()
                    tmp.append(i)
                    que.append(tmp)
                else:
                    que.append([i])
            while que.__len__() != 0:
                curs = que.pop()
                for cur in curs:
                    r_min = -1
                    if que.__len__() > 0:
                        # 弹出后,如果栈中还有值,说明栈弹出的第一个值是 cur 左边比它小的值中离它最近的值
                        l_min = que[-1][-1]
                    else:
                        l_min = -1
                    res.append([cur,l_min,r_min])
            return res

结果输出:

输入:[2, 4, 5, 5, 4, 3, 8, 55, 6, 3, 9, 1]
输出:[[2, 1, 4], [3, 1, 4], [1, 0, 5], [4, 0, 5], [7, 6, 8], [6, 5, 8], [8, 5, 9], [10, 9, 11], [5, 0, 11], [9, 0, 11], [0, -1, 11], [11, -1, -1]]

题四:​​​​​​​

题目:    给定一个只包含正整数的数组arr,arr中任何一个子数组sub,一定都可以算出     value =(sub累加和)*(sub中的最小值),那么,在所有子数组中,最大的    value是多少分析:    以arr[i]为子数组sub的最小值往数组arr左右两边扩充,使得len(sub)最大,    则可以获得 以arr[i]为最小值的子数组sub,使得value最大,可以使用    get_closed_min 中的结果,来获取sub的最大边界(以arr[i]为最小值)

代码实现

def get_max_value(self,arr):
        """
        题目:
            给定一个只包含正整数的数组arr,arr中任何一个子数组sub,一定都可以算出 value =(sub累加和)*(sub中的最小值),
            那么,在所有子数组中,最大的value是多少
        分析:
            以arr[i]为子数组sub的最小值往数组arr左右两边扩充,使得len(sub)最大,则可以获得 以arr[i]为最小值的子数组sub,使得value最大
            可以使用get_closed_min 中的结果,来获取sub的最大边界(以arr[i]为最小值)
        """
        res = []
        close_value = obj.get_closed_min(arr)
        # print(len(arr),len(res))
        close_value = sorted(close_value, key=lambda x: x[0])
        for tmp in close_value:
            # print(tmp)
            index = tmp[0]
            # 因为get_closed_min 获取的是左右两边比 arr[i] 小而且离离 arr[i] 最近值的下标,故所以要把这两个值剔除掉,又因为python数组切片左闭右开的特性,故right要+1
            left = tmp[1]+1
            right = tmp[2]+1-1
            if left <= -1:
                left = index
            if right <= -1 :
                right = index
            # print(tmp)
            value = arr[index]*sum(arr[left:right])
            # print(arr[index],arr[left:right],value)
            res.append(value)
        max_value = max(res)
        return max_value

结果输出:​​​​​​​

输入: [2, 4, 5, 5, 4, 3, 8, 55, 6, 3, 9, 1]
输出:3025

总结:单调栈和窗口都是利用结构内有序 和 新进来的数的下标比结构内的数的下标更晚过期的特性。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值