📌  相关文章
📜  在旋转数组中查找给定长度的最大和连续子数组的查询(1)

📅  最后修改于: 2023-12-03 15:08:03.265000             🧑  作者: Mango

在旋转数组中查找给定长度的最大和连续子数组的查询

在一个旋转有序数组中查找给定长度的最大和连续子数组是一个经典的问题,它要求我们在一个数组中找到一个指定长度的子数组,使得该子数组的元素和最大。

问题描述

给定一个旋转有序数组 nums 和一个整数 k,请你计算数组中长度为 k 的最大连续子数组的和并返回该值。

示例

示例 1:

输入:nums = [3,4,5,6,7,1,2], k = 3
输出:18
解释:子数组 [6,7,1] 的全部元素可以加起来得到 18 。

示例 2:

输入:nums = [2,1,3], k = 3
输出:6
解释:子数组 [2,1,3] 的全部元素可以加起来得到 6 。
条件限制
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^4
  • 1 <= k <= nums.length
解题思路
方法一:暴力枚举

首先考虑最朴素的方法,即枚举数组 nums 中所有长度为 k 的子数组,计算它们的和并取最大值。时间复杂度为 $O(nk)$,其中 $n$ 是数组 nums 的长度。

def max_sum_of_k(nums, k):
    n = len(nums)
    res = float("-inf")
    for i in range(n - k + 1):
        s = sum(nums[i:i+k])
        res = max(res, s)
    return res
方法二:滑动窗口

可以发现,对于每个固定长度的子数组,它与其相邻的子数组之间有 $k - 1$ 个元素是相同的,因此可以通过滑动窗口的方法避免重复计算。具体来说,定义两个指针 leftright 分别表示滑动窗口的左右边界,初始时 left = right = 0。每次将窗口右边界向右移动一步,同时将窗口左边界向右移动一步以保持窗口长度不变,直到右边界到达数组末尾为止,过程中记录窗口内所有子数组的和的最大值即可。时间复杂度为 $O(n)$,其中 $n$ 是数组 nums 的长度。

def max_sum_of_k(nums, k):
    n = len(nums)
    res = float("-inf")
    s = sum(nums[:k])
    for i in range(k, n):
        s += nums[i] - nums[i-k]
        res = max(res, s)
    return res
方法三:优化的滑动窗口

方法二依然存在一些冗余计算,例如当当前遍历的右端点 i 不在旋转点前面时,窗口中的每个元素都需要被累加一次。因此我们考虑去掉这样的冗余操作,具体来说:

  1. 定位旋转点 rot,可以采用二分查找的方法实现,时间复杂度为 $O(log\ n)$;
  2. 在确定了旋转点 rot 后,将数组分为两段,分别进行滑动窗口计算,时间复杂度为 $O(k + log\ n)$。
def find_rot(nums):
    n = len(nums)
    left, right = 0, n-1
    while left < right:
        mid = left + (right - left) // 2
        if nums[mid] < nums[right]:
            right = mid
        else:
            left = mid + 1
    return left

def max_sum_of_k(nums, k):
    n = len(nums)
    rot = find_rot(nums)
    res = float("-inf")
    # 计算窗口左半边的和
    s = sum(nums[rot-k:rot])
    for i in range(k, rot):
        s += nums[i] - nums[i-k]
        res = max(res, s)
    # 计算窗口右半边的和
    s = sum(nums[rot:rot+k])
    for i in range(rot+k, n):
        s += nums[i] - nums[i-k]
        res = max(res, s)
    return res
总结
  • 本题是一个经典的滑动窗口问题,利用滑动窗口可以将时间复杂度优化到 $O(n)$ 级别;
  • 当题目中给出了特殊条件时,如旋转有序数组,可以利用特殊条件进行进一步优化,例如本题中的方法三。