我试图解决以下问题:
给定一个只包含0和1的二进制数组,找到包含0和1的等号的最大子数组。
示例:
输入:arr [] = {1,0,1,1,1,0,0,0,1} 输出:1到8(输出子数组的起始和结束索引)
我只能想到一个O(n ^ 2)解决方案(即在每个子位置启动数组,然后检查所有剩余元素是否具有相同数量的0和1)的明显方法。
有人可以为这个问题找到更好的解决方案吗?
答案 0 :(得分:0)
我相信这可以使用权重平衡二叉树结构在O(n)中解决。
答案 1 :(得分:0)
关于措辞的一个小小的注释:你说找到 最长的子阵列,这意味着唯一性,但即使在你的例子中也有不止一个(0到7或1到8)。它将更好地表示为“找到最大长度的 a 子阵列”或类似的。但这不是问题。
对于更快的算法,首先通过用-1替换0的每个实例来定义新数组swap
。这可以在O(n)时间内完成。对于你的例子,我们会有
1, -1, 1, 1, 1, -1, -1, -1, 1
现在定义另一个数组sum
,使sum[i]
是所有值swap[0], swap[1], ..., swap[i]
的总和。等价地,
sum[0] = swap[0];
for i > 1, sum[i] = sum[i-1] + swap[i]
O(n)时间再次出现。所以你的例子变成了
1, 0, 1, 2, 3, 2, 1, 0, 1
现在进行观察。如果1的数量等于子阵列(arr[i], ..., arr[j])
中的0的数量,那么在第一个新数组中,1s将取消相应的-1,因此所有值(swap[i], ..., swap[j])
的总和将相等到0.但这等于
swap[0] + swap[1] + ... + swap[j] - (swap[0] + swap[1] + ... + swap[i-1]),
又等于
sum[j] - sum[i-1].
虽然注意,如果i
等于0
,我们必须小心,否则我们不在数组的范围内。这是一个易于实现的检查。
现在我们已将问题简化为在sum[j] - sum[i-1]
等于0时查找。但这相当于找到值j
和i
,使sum[j] = sum[i-1]
。
由于我们知道sum
中的所有值都位于-n和n之间(其中n是初始数组的大小),您现在可以创建另一对数组min
和{ {1}}大小为2n + 1。此处,max
和min
的索引对应max
的潜在值,其中sum
将包含min[0]
的最小索引i
,sum[i] = -n
将包含min[1]
的最小索引i
,依此类推。同样,max将保持最大的索引。这也可以在线性时间内实现。完成此步骤后,sum[i] = -n+1
和max[i]
将对应min[i]
的值。
现在你所要做的就是找到sum[min[i]] = i = sum[max[i]]
的最大值,这将从上面max[k] - min[k]
和i = min[k] + 1
给出,包含相同数字的最大子数组的索引0和1。这也是O(n)。
我有点粗略地描述了这一点,所以当i = 0时你必须小心,但这很容易解释。然而,每一步都是O(n),所以你的算法更有效率。