📜  总和最接近K的子数组(1)

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

题目简介

给定一个整数数组和一个目标值 k,找到该数组中和最接近 k 的子数组。返回这个子数组的和。

示例:

输入: nums = [1,5,7,9], k = 12
输出: 12
解释: 子数组 [1,5,7] 的和为 13,是所有和最接近 12 的子数组中最接近的。(注意是连续子数组)

解题思路

题目中要求找到和最接近 k 的子数组,所以遍历数组的同时,需要记录当前的子数组和 sum,即从数组的某个位置开始向右一直累加的和。

假定当前子数组从下标 i 到 j,子数组的和为 sum,则 sum 与 k 的差值 diff 的绝对值为 |sum - k|。由于要找到最接近的子数组和,因此需要找到 diff 最小的子数组。

为了达到这个目的,我们可以使用一个变量 min_diff 来记录已经找到的与 k 最接近的子数组的和 sum,然后遍历整个数组,对于每个子数组的和 sum,计算 sum 与 k 的差值 diff,如果 diff 小于 min_diff,则更新 min_diff 和最终的结果 res。

在遍历完整个数组后,res 就是与 k 最接近的子数组的和。

代码实现

以下是 Python 代码实现,时间复杂度为 O(N^2):

class Solution:
    def closestSubarraySum(self, nums: List[int], k: int) -> int:
        n = len(nums)
        res = float('inf')
        min_diff = float('inf')
        for i in range(n):
            for j in range(i, n):
                sub_sum = sum(nums[i:j+1])
                diff = abs(sub_sum - k)
                if diff < min_diff:
                    min_diff = diff
                    res = sub_sum
        return res

优化思路

上述算法的时间复杂度为 O(N^2),对于大规模数据的情况,可能会出现超时问题。因此,我们需要考虑优化算法的时间复杂度。

一种常见的优化思路是使用前缀和。对于一个固定的下标 j,可以先预处理出从 0 到 j 的元素的和 prefix_sum[j],然后对于任意的 i(0 <= i <= j),从下标 i 到 j 的元素的和就可以通过 prefix_sum[j] - prefix_sum[i-1] 来计算。

这样,就可以在 O(1) 的时间复杂度内计算出任意子数组的和。因此,我们可以将时间复杂度优化为 O(N^2)。

代码实现

以下是使用前缀和优化后的 Python 代码实现,时间复杂度为O(N^2):

class Solution:
    def closestSubarraySum(self, nums: List[int], k: int) -> int:
        n = len(nums)
        res = float('inf')
        min_diff = float('inf')
        prefix_sum = [0] * (n + 1)
        for i in range(1, n+1):
            prefix_sum[i] = prefix_sum[i-1] + nums[i-1]
        for i in range(n):
            for j in range(i, n):
                sub_sum = prefix_sum[j+1] - prefix_sum[i]
                diff = abs(sub_sum - k)
                if diff < min_diff:
                    min_diff = diff
                    res = sub_sum
        return res

更优解法

前缀和的时间复杂度为 O(N),可以将算法的时间复杂度降低到 O(N^2)。但是,我们还可以考虑更优秀的解法。

对于一个固定的下标 j,假设我们已经找到了前 j 个元素中与 k 最接近的子数组的和 sum,则对于下标 j+1,我们可以通过两种方法来计算与 k 最接近的子数组的和:

  • 方法一:从前 j+1 个元素中找到与 k 最接近的子数组的和 sum‘,则前 j+1 个元素中与 k 最接近的子数组就是 sum‘。
  • 方法二:以 j+1 为结尾的子数组中必定包括下标 j,因此我们可以遍历前 j 个元素,记录以 i 为结尾的子数组的和 sub_sum,并计算 sub_sum 与 k 的差值 diff。这样,对于下标 j+1,我们只需要找到所有以 i 为结尾的子数组中与 k 最接近的子数组,然后取这些子数组中的最小值 min_diff,并令 sum = prefix_sum[j+1] - min_diff,即为前 j+1 个元素中与 k 最接近的子数组的和。

由于方法二只需要遍历前 j 个元素,可以在 O(N) 的时间复杂度内计算出以 j+1 为结尾的子数组中与 k 最接近的子数组,因此算法的时间复杂度为 O(N^2)。

代码实现

以下是优化后的 Python 代码实现,时间复杂度为O(N^2):

class Solution:
    def closestSubarraySum(self, nums: List[int], k: int) -> int:
        n = len(nums)
        res = float('inf')
        min_diff = float('inf')
        prefix_sum = [0] * (n + 1)
        for i in range(1, n+1):
            prefix_sum[i] = prefix_sum[i-1] + nums[i-1]
        for j in range(n):
            sum = prefix_sum[j+1]
            for i in range(j+1):
                sub_sum = sum - prefix_sum[i]
                diff = abs(sub_sum - k)
                if diff < min_diff:
                    min_diff = diff
                    res = sub_sum
        return res

总结

本题主要考察数组的遍历和前缀和的应用,并且需要注意时间复杂度优化的问题。通过本题的练习,可以提高数组操作和算法设计的能力。