Processing math: 100%

三个求和为0的最大子数组方法

问题:
设有一个数组 a[1],a[2],a[3],\ldots,a[n]
求这个数组的最长子数组,这个子数组元素和为 0

例如,对于数组 \{1,3,8,-11,0,-7,12\}
其和为 0 的最长子数组为 \{3,8,-11,0\}

方法一:

既然所求的是最大子数组,我们不妨从长往短找,以便减少查找次数

对于数组 a[1],a[2],a[3],\ldots,a[n]

  1. 其最长子数组为其本身,所以先计算
    a[1]+a[2]+\cdots+a[n]
    判断其是否为 0
  2. 其次长子数组为 a[1],a[2]…,a[n-1] a[2],a[3]…,a[n]
    分别计算这两个子数组和
  3. 依次减少数组长度,直到找到最长子数组

需要注意的是:

  • 若找到长为k的最长子数组,不能立刻停止计算
    而要继续计算完剩下长度为k的子数组是否和为0,防止最长子数组不唯一
  • 分析该种计算方法,在最坏情况下(即最长子数组不存在),需要的计算步骤为:
    1+2+3+\cdots+n=\Theta(n^2)
    即时间复杂度是数组长度的平方

这种算法相信是大家都能想到的,思想是“暴力枚举”

那么有没有一种方法可以简化寻找次数呢?

方法二:

对于数组 a[1],a[2],\ldots,a[n] 我们想到:能不能通过分治的方法简化运算呢?

我们可以将 a[1],a[2],\ldots,a[n] 分为 a[1],a[2],\ldots,a[n/2] a[n/2+1],\ldots,a[n] 两个部分
再分别求两部分的和为 0 的最长子数组
再找到我们刻意忽略的跨过 a[n/2] a[n/2+1] 的最长和为0子数组
最后取较大者

这种计算方法需要以下步骤:

  1. a[1],a[2],\ldots,a[n] 分为 a[1],a[2],\ldots,a[n/2] a[n/2+1],\ldots,a[n]
  2. 找到 a[1],a[2],\ldots,a[n/2] a[n/2+1],\ldots,a[n] 的和为 0 的最长子数组
  3. 找到跨过 a[n/2] a[n/2+1] 的最长和为 0 子数组
  4. 取最大者

注:

  • n/2 仍较大,可继续把 a[1],a[2],\ldots,a[n/2] 再分下去
  • 事实上,真正需要计算的只有步骤3,即其实只需要计算如何“合并”即(在此不予证明)
    该种算法时间复杂度比枚举稍有优化,为 \Theta(n\log n)

方法3:

还能继续简化计算步骤么?可以的:

如果我们令
S_n=a[1]+a[2]+\cdots+a[n]

\begin{align} S_1 &= a[1]\\ S_2 &= a[1]+a[2]\\ S_3 &= a[1]+a[2]+a[3]\\ &\cdots \end{align}

那么我们可以得到一个明显的结论:

如果 S_i = S_j ,那么 a[i+1]+a[i+2]+\cdots+a[j] = 0

如:
S_1=1 \quad S_2=5 \quad S_3=8 \quad S_4=1

a[2]+a[3]+a[4]=0

用这种算法,我们只需要对 a[n] 的数组进行一次加法运算并记录(甚至不需要记录)相同 S_i 出现的位置
就可以很快的计算出和为 0 的最长子数组

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注