617 | Maximum 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))