leetcode专项刷题_数组(3)_子数组最大平均数 I/最大子序和/数组的度

子数组最大平均数 I

问题描述

给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数

输入: [1,12,-5,-6,50,3], k = 4
输出: 12.75
解释: 最大平均数 (12-5-6+50)/4 = 51/4 = 12.75

代码实现

# 暴力法,最容易理解
# 思路: 截取每一个长度为k的nums数据,求和放到新的列表中,返回最大值,求解
class Solution:
    def findMaxAverage(self, nums, k):
        if k == len(nums):
            return max(nums) / k
        ret = []
        for i in range(len(nums) - k + 1):
            ret.append(sum(nums[i: i + k]))
        print(ret)
        return max(ret) / k

# 思路:
# 复制原表,存放k个连续值之和
#	说白了就是前4个值等于1,2,3,4索引位置的值相加
# 然后下一个就是2,3,4,5位置相加,那么就可以理解为 :上一个前4个值之和 - 第一个值 + 当前值
# 以此类推,tmp是为了保存上一个需要被记录的
import copy
class Solution:
    # 复制一个样本,用来专门保存前一个前4个值之和的第一个值
    def findMaxAverage(self, nums, k):
        if k == 1:
            return max(nums)
        nums_copy = copy.deepcopy(nums)
        for i in range(len(nums_copy) - k + 1):
          # 0没有意义,只是为了拿另一个值
            tmp = nums[i - 1] if i >= 1 else 0
            if i == 0:
                nums_copy[i] = sum(nums_copy[i: i + k])
            else:
                nums_copy[i] = nums_copy[i - 1] - tmp + nums_copy[i + k - 1]
        return max(nums_copy[:len(nums_copy) - k + 1]) / k

# 累计求和
# 思路:
# 假如 k=4,那么前四个值的和就等于nums[0],nums[1],nums[2],nums[3]之和
#					那么第5个值为结束的求和就等于前5个值的和减去前一个的值
#					那么第6个值为结束的求和就等于前6个数之和减去前两个数之和
# 				····以此类推
# 之后就如上面所说,nums[current_index]-nums[current - k]就是从索引值为k之后的求和值,但是不能赋值给nums[current_index],因为一旦赋值,后面的计算就有可能会用到前面更改的值,而不是最开始的求和值了
# 最后就要比较下k-1位置的值和k位置开始到最后的值的大小
class Solution:
    def findMaxAverage(self, nums, k):
      	# 将每个索引对应的值 更新为 到当前索引求和
        for i in range(1, len(nums)):
            nums[i] = nums[i - 1] + nums[i]
        # 
        ret = nums[k - 1]
        for j in range(k, len(nums)):
            ret = max(nums[j] - nums[j - k], ret)
        return ret / k

最大子序和

题目描述

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6

代码实现

# 动态求和
# 思路:
# 和上题的整体思路差不多,不同点是不知道k的大小
# 想办法算出数组每个位置所能求的的最大值,那么就是说 “应该比较 (当前索引对应的值+当前索引之前的最大值) 和当前索引值对应的大小”,谁大保留谁,直至遍历结束,每个位置保存的都是计算到每个索引值的最大值
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        for i in range(1,len(nums)):
            nums[i] = max(nums[i-1]+nums[i],nums[i])
        return max(nums)

数组的度

题目描述

给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。

你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度

输入: [1, 2, 2, 3, 1]
输出: 2
解释: 
输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.

代码实现

# 我的版本
# 思路: 
# 1.肯定先统计数量,我最开始用count函数,哎呀我去,这个效率真的是不行,后来改用字典.get方法计数(Counter方法应该也行,不知道效率,没测试)
# 2.思考如何统计包含度的列表长度,其实可以想像出度所指向的值,一定是首尾都是这个数
#   2.1 假如像上面的例子一样,度为2,那么我们看1这个数,一定是首尾都是1,那么此时长度为5,而2作为首尾元素长度为2
#   2.2 所以就可以归纳出,要先找到为度的值,然后找出nums中的首尾索引,相减 + 1就行了
	
class Solution:
    def findShortestSubArray(self, nums):
        # d = {num: nums.count(num) for num in nums}
        d = {}
        for num in nums:
            d[num] = d.get(num, 0) + 1
        # 计算du值
        du = max(d.values())
        du_list = []
        # 所有满足最大du的原数据汇总
        for key, value in d.items():
            if value == du:
                du_list.append(key)
        min_l = len(nums)
        # 计算最大索引和最小索引
        for i in du_list:
            start_index = nums.index(i)
            for j in range(len(nums) - 1, -1, -1):
                if nums[j] == i:
                    end_index = j
                    break
            min_l = min(min_l, end_index - start_index + 1)
        return min(min_l)
      
# 官方版本(思路是一样的, 字典用的妙,由此提升了3倍的性能)
class Solution(object):
    def findShortestSubArray(self, nums):
      	# left用来记录每个元素第一次出现的索引,right用来记录每个元素最后一次的索引, count用来计数
        left, right, count = {}, {}, {}
        for i, x in enumerate(nums):
            if x not in left: left[x] = i
            right[x] = i
            count[x] = count.get(x, 0) + 1
				# 最不济无非就是整个列表长度
        ans = len(nums)
        # 确定度数
        degree = max(count.values())
        # 计算长度
        for x in count:
            if count[x] == degree:
                ans = min(ans, right[x] - left[x] + 1)

        return ans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值