lintcode 617 Maximum Average Subarray

博客围绕lintcode 617最大平均子数组问题展开,虽未给出具体内容,但可推测是关于该问题的分析、解法等信息技术相关探讨。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

617Maximum Average Subarray长度大于等于k的连续元素的最大平均值,比leet643再多加一个局部最大ll, l变化起点, ll变化终点。

 

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Feb 14 15:41:08 2018

@author: vicky
"""
#法1:o(n2)
class Solution:
    """
    @param: nums: an array with positive and negative numbers
    @param: k: an integer
    @return: the maximum average
    """
    def maxAverage(self, nums, k):
        # write your code here
        g=sum(nums[0:k])/k
        l=sum(nums[0:k])/k
        for i in range(len(nums)-k):
            ll=l #l变换数组开头,ll变换数组末尾
            for j in range(i+k,len(nums)):
                ll=(ll*(j-i)+nums[j])/(j-i+1) #ll=从i开始的大于k个数字的平均值
                g=max(g,ll)
            l=(l*k-nums[i]+nums[k+i])/k #l=从i开始的k个数字的平均值
        g=max(g,l)
        return g

nums=[1,12,-5,-6,50,3]
k=3
print(Solution().maxAverage(nums,k))


二分法:
第一类: 需查找和目标值完全相等的数
eg:数组nums=[2, 4, 5, 6, 9],target = 6,需要返回3
#left,right,mid都是数组index
    left=0
    right=len(nums)
    while left<right:
        mid=(right+left)/2
        if nums[mid] == target: 
            return mid
        elif nums[mid] < target: 
            left=mid #target在右区间则舍去左区间
        else:
            right=mid #target在左区间则舍去右区间
        
第二类:查找第一个不小于目标值的数
有时候要查找的目标值不一定会在数组中出现,也有可能是跟目标值相等的数在数组中并不唯一,而是有多个,
那么这种情况下nums[mid] == target这条判断语句就没有必要存在。
eg:在数组nums=[2,4,5,5,6]中查找数字5,就会返回数字5的位置3
    left=0
    right=len(nums)
    while left<right:
        mid=(right+left)/2
        if nums[mid] < target:
            left=mid+1 #target在右区间则找到了第一个不小于目标值的数,break
            #加1是为了如果left=right,就break,返回right;如果不加1就会是死循环
        else:
            right=mid #target在左区间则舍去右区间
    return right

第三类: 查找第一个大于目标值的数,可变形为查找最后一个不小于目标值的数
eg:在数组nums=[2,4,5,5,6]中查找数字5,就会返回数字6的位置4
    left=0
    right=len(nums)
    while left<right:
        mid=(right+left)/2
        if nums[mid] <= target: #唯一变的地方,加上等号
            left=mid+1 
        else:
            right=mid 
    return right

第四类:用子函数当作判断关系,并不是之前三类中简单的数字大小的比较
eg:这一题
思路:http://www.cnblogs.com/grandyang/p/8021421.html
所求的最大平均值一定是介于原数组的最大值和最小值之间,
所以目标是用二分法来快速的在这个范围内找到要求的最大平均值,
初始化left为原数组的最小值,right为原数组的最大值,然后mid就是left和right的中间值,
难点就在于如何得到mid和要求的最大平均值之间的大小关系,从而判断折半方向:
    如果我们已经算出来了这个最大平均值maxAvg,那么对于任意一个长度大于等于k的数组,
    如果让每个数字都减去maxAvg,那么得到的累加差值一定是小于等于0的。
    比如[1, 2, 3, 4]   k = 2,最大平均值maxAvg = 3.5,
    所以任何一个长度大于等于2的子数组每个数字都减去maxAvg的差值累加起来都小于等于0,
    只有产生这个最大平均值的子数组[3, 4],算出来才正好等于0,其他都是小于0的。
    那么我们可以根据这个特点来确定折半方向,让数组中的每一个数字都减去mid,然后算差值的累加和,
    一旦发现累加和大于0了,那么说明我们mid比maxAvg小,这样就可以判断方向了。

我们建立一个累加和数组sums,然后求出原数组中最小值赋给left,最大值赋给right,
题目中说了误差是1e-5,所以我们的循环条件就是right比left大1e-5,
然后我们算出来mid,定义一个minSum初始化为0,布尔型变量check,初始化为false。
然后开始遍历数组,先更新累加和数组sums,
注意这个累加和数组不是原始数字的累加,而是它们和mid相减的差值累加。
我们的目标是找长度大于等于k的子数组的平均值大于mid,
由于我们每个数组都减去了mid,那么就转换为找长度大于等于k的子数组的差累积值大于0。
我们建立差值累加数组的意义就在于通过sums[i] - sums[j]来快速算出j和i位置中间数字之和,
那么我们只要j和i中间正好差k个数字即可,然后minSum就是用来保存j位置之前的子数组差累积的最小值,
所以当i >= k时,我们用sums[i - k]来更新minSum,这里的i - k就是j的位置,
然后判断如果sums[i] - minSum > 0了,说明我们找到了一段长度大于等k的子数组平均值大于mid了,
就可以更新left为mid了,我们标记check为true,并退出循环。
在for循环外面,当check为true的时候,left更新为mid,否则right更新为mid,

#法2:二分法
class Solution:
    """
    @param: nums: an array with positive and negative numbers
    @param: k: an integer
    @return: the maximum average
    """
    def maxAverage(self, nums, k):
        if sum(nums)==0:
            return 0
        left=min(nums)
        right=max(nums)
        sums=[0 for x in range(0, len(nums)+1)] #长度为n+1的全0列表
            minSum=0
            mid=(right+left)/2 #目标是找长度大于等于k,且平均值大于mid的子数组
            check=False
            for i in range(1,len(nums)+1): #i=1,....,n
                sums[i]=sums[i-1]+nums[i-1]-mid #sums表示nums和mid相减的差值的累加和数组
        while right-left>1e-5:
                if i>=k:
                    minSum=min(minSum,sums[i-k]) #minSum就是用来保存j位置之前的子数组差累积的最小值
                if i>=k and sums[i]>minSum:  #说明找到了一段长度大于等k的子数组平均值大于mid了
                    check=True
                    break #注意break只能调出一层循环
            if check is True: #用break和if搭配跳出双层循环
                left=mid
            else:
                right=mid
        return left

##把上诉累加和数组sums变成单独的数字和sums和preSum
#    def maxAverage(self, nums, k):
#        left=min(nums)
#        right=max(nums)
#        while right-left>1e-5:
#            minSum=0
#            sums=0
#            preSum=0
#            mid=(right+left)/2
#            check=False
#            for i in range(len(nums)):
#                sums=sums+nums[i]-mid
#                if i>=k:
#                    preSum=preSum+nums[i-k]-mid
#                    minSum=min(minSum,preSum)
#                if i>=k-1 and sums>minSum:
#                    check=True
#                    break #注意break只能调出一层循环
#            if check is True: #用break和if搭配跳出双层循环
#                left=mid
#            else:
#                right=mid
#        return left


nums=[1,12,-5,-6,50,3]
k=3
print(Solution().maxAverage(nums,k))

#class Solution:
#    """
#    @param: nums: an array with positive and negative numbers
#    @param: k: an integer
#    @return: the maximum average
#    """
#    def maxAverage(self, nums, k):
#        """
#        :type nums: List[int]
#        :type k: int
#        :rtype: float
#        """
#        def getDelta(avg, nums, k):
#            accu = [0.0] * (len(nums) + 1)
#            minval_pos = None
#            delta = 0.0
#            for i in range(len(nums)):
#                accu[i+1] = nums[i] + accu[i] - avg
#                if i >= (k-1):
#                    if minval_pos == None or accu[i-k+1] < accu[minval_pos]:
#                        minval_pos = i-k+1
#                    if accu[i+1] - accu[minval_pos] >= 0:
#                        delta = max(delta, (accu[i+1] - accu[minval_pos]) / (i+1 - minval_pos))
#            return delta
#
#        left, delta = min(nums), float("inf")
#        while delta > 1e-5:
#            delta = getDelta(left, nums, k)
#            left += delta
#        return left
#
#nums=[1,12,-5,-6,50,3]
#k=3
#print(Solution().maxAverage(nums,k))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值