查找和为0的最大子数组。

17 Mar 2025 | 6 分钟阅读

子数组是数组的连续部分。查找具有特定属性的最大长度子数组,例如零和,在计算机科学和数学中有各种应用。例如,查找最大长度的零和子数组有助于金融分析以检测相互抵消的欺诈交易。同样,它有助于在生物信息学和物理学等领域检测数据不一致性。

在本文中,我们将解决在给定整数数组中查找和为0的最大子数组长度的问题。例如,在集合[10, 2, -5, 1, 6]中,最长的零和子数组是[2, -5, 1],长度为3。解决此问题的有效方法是使用哈希表,并在从左到右遍历数组时跟踪累积和。

关键思想依赖于这样一个事实:如果直到两个索引i和j的前缀和相同,则索引i和j之间的元素之和为0。我们利用此属性,通过在哈希表中存储前缀和及其索引来有效地找到最长零和子数组的长度。

本文的提纲如下:

  1. 正式问题陈述
  2. 算法逻辑说明
  3. Python代码实现
  4. 时间和空间复杂度分析
  5. 使用样本数组作为输入的示例运行。

方法 1:蛮力法

查找最长零和子数组的最直接方法是生成给定数组的所有可能子数组并检查它们的和。这本质上是一种穷举搜索,探索标记数组中子序列的起始点和结束点的所有组合。更正式地说,我们有两个嵌套循环 - 外循环选择一个起始元素,内循环考虑所有可能的结束元素来构造从外循环选择的起始元素开始的子数组。我们在内循环中维护一个运行总和,如果总和变为0,我们就更新迄今为止找到的最大长度。这种蛮力技术的时间复杂度为O(n^2),因为我们有迭代n个元素的嵌套循环。尽管实现简单,但这种二次时间复杂度使其对于大型输入数组不可行。我们将很快讨论一种具有更好时间复杂度的更有效方法。但首先,让我们通过一些示例代码来回顾蛮力方法,然后进行运行时间分析。

输出

Find the largest subarray with 0 sum

说明

  1. 定义largest_zero_subarray函数,该函数以输入数组(arr)作为参数。
  2. 初始化一个变量max_len来存储结果(最长零和子数组的长度)。将其初始化为0。
  3. 从索引0到len(arr)开始外循环。此循环选择所有可能子数组的起始元素。
  4. 在开始内循环之前,将curr_sum初始化为0。curr_sum将存储当前子数组的和。
  5. 从外循环的当前索引到len(arr)开始内循环。此循环考虑外循环为最近选取的起始元素的所有可能的结束元素,并找到和。
  6. 在内循环中,不断地将当前元素添加到curr_sum。
  7. 如果任何时候curr_sum变为0,则将max_len更新为当前长度(即j-i+1)与现有max_len之间的最大值。
  8. 内循环结束后,返回最终的max_len,其中包含零和子数组的最大长度。
  9. 用法示例:定义数组并调用largest_zero_subarray函数,传入该数组。打印返回的结果。

方法2:前缀和技术

查找最长零和子数组的蛮力方法运行时间为二次复杂度,这对于大量输入可能是不可行的。我们可以使用前缀和技术结合哈希来优化它。前缀和概念依赖于这样一个事实:如果到两个索引i和j的前缀和相等,则索引i和j之间的元素之和为0。我们可以通过一次遍历数组并跟踪当前前缀和来利用这一点。如果我们再次遇到任何前缀和值,我们可以推断出先前出现索引和当前实例之间的元素之和为0。我们将唯一前缀和的索引存储在哈希表中。将当前索引与先前保存的索引进行比较,可以得到当前零和子数组的长度。此技术将时间减少到O(n),因为数组只遍历一次。接下来,我们将介绍实现此前缀和哈希逻辑的高效Python代码,然后进行运行时间分析。但首先,在继续之前,理解关键 - 将相同的前缀和链接起来以推断零和子数组至关重要。

输出

Find the largest subarray with 0 sum

说明

  1. 定义largest_zero_subarray函数,该函数以输入数组(arr)为参数
  2. 将max_len初始化为0,用于存储结果子数组的长度。
  3. 创建一个空字典prefix_sum,以存储前缀和作为键,以它们的索引作为值。
  4. 将curr_sum初始化为0,用于存储当前前缀和。
  5. 从0到数组长度开始循环。
  6. 不断将当前元素添加到curr_sum
  7. 如果当前元素本身为0,则将max_len更新为1。如果它是0
  8. 如果当前的curr_sum变为0,则将max_len更新为当前索引+1
  9. 检查当前的curr_sum是否已存在于prefix_sum字典中。
  10. 如果是,则当前的curr_sum之前也出现过。因此,通过取它们的差值,找到前一个出现索引与当前索引之间的长度。如果此差值更大,则更新max_len。
  11. 如果curr_sum不在字典中,则将其与当前索引一起插入。
  12. 最后,返回计算出的max_len
  13. 用法示例 - 将输入数组传递给函数并打印返回的结果

方法3:使用集合(Set)

在前面的前缀和技术中,我们使用哈希表来存储在遍历数组时遇到的前缀和。这使我们能够通过链接精确的前缀和,在 O(1) 时间内有效地找到零和子数组。然而,可以使用 Set 来优化哈希表中的搜索,Set 提供了常数时间包含检查。

关键思想是将遇到的所有前缀和插入 Set 中。检查 Set 中的任何总和的存在将表明它是否曾出现过。因此,精确的总和表示从先前出现索引到当前实例的零和子数组。Set 提供平均情况 O(1) 的搜索时间,从而实现线性时间复杂度。

使用 Set 而不是哈希表的优势在于它消除了不必要的索引存储。我们只需要检查当前前缀和是否存在。如果它存在于 Set 中,我们就找到了一个零和子数组;否则,我们只插入当前总和。这种空间优化对于大型数组很有益。现在,让我们通过应用此逻辑的 Python 代码进行演练,并分析其运行时间复杂度。

输出

Find the largest subarray with 0 sum

说明

  1. 定义largest_zero_subarray函数,该函数接受输入数组。
  2. 初始化一个变量max_len来存储结果(最长零和子数组的长度)。
  3. 将curr_sum初始化为0,用于存储当前运行总和。
  4. 创建一个集合sum_set,并将其0插入其中。此Set将存储迄今为止看到的唯一总和。
  5. 从索引0到数组长度开始遍历数组。
  6. 通过将当前元素添加到curr_sum来不断更新curr_sum。
  7. 检查当前的curr_sum是否已存在于sum_set集合中。
  8. 如果是,我们找到了一个零和子数组,它从这个curr_sum先前出现过的索引到当前索引。更新max_len。
  9. 如果curr_sum不存在,则将其插入Set中。
  10. 最后,返回存储在max_len中的最大长度。
  11. 使用方法 - 将输入数组传递给函数并打印返回的结果