找到没有溢出的整数数组的总和

时间:2013-01-27 07:11:58

标签: java algorithm

给定一个整数数组(正数和负数),每个数组最多有K位(加上符号位),并且已知数组中所有整数的总和也至多有K位(加上标志位)。设计一种计算数组中整数之和的算法,所有中间和也最多具有K位(加上符号位)。 [提示:找到你应该添加正数和负数的顺序]。

这是面试材料而不是作业的问题

我实际上正在考虑创建两个单独的数组,一个用于正数,另一个用于负数,对它们进行排序,然后添加两个数据,以便大多数负数被添加到大多数正数...但这似乎有O(nlogn)时间复杂度(排序)和O(n)空间复杂度>请帮忙!

3 个答案:

答案 0 :(得分:7)

首先请注意,即使你让立即结果溢出,如果可以表示最终结果也总是正确的。这是因为任何大小的整数类型都像大多数语言中的循环组一样,包括Java(不是在C中,整数溢出是未定义的行为,C#,它能够引发溢出异常)。

如果想要防止溢出,请按照以下方式执行此操作:

  • 将数组就地分割为其负数条目(按任意顺序)和正数条目(按任意顺序)。零可以在任何地方结束。换句话说,执行一个快速排序步骤,枢轴为零。

  • ni指向数组的开头(负数条目所在的位置)。

  • pi指向数组的末尾。
  • sum为零。
  • pi >= ni

    • 如果sum为否定
      • arr[pi]添加到sum
      • 如果arr[pi]为否定(我们已用完正加数)并且sum为正(出现溢出),则结果会溢出。
      • 减少pi
    • 否则
      • arr[ni]添加到sum
      • 如果arr[ni]为正数且sum为负数,则结果会溢出。
      • 增量ni
  • 最后,检查sum是否超过K位。如果是,则声明结果溢出。

答案 1 :(得分:2)

主要思想是使用两个索引迭代数组:正面和负面元素。当总和为负时,我们将搜索下一个正元素(使用相应的迭代器)以添加到总和,否则 - 添加到负数。

此代码应该有效:

public final class ArrayAdder {
  @NotNull
  private final int[] array;

  private int sum;

  public ArrayAdder(@NotNull int[] array) {
    this.array = array;
  }

  public int sum() {
    sum = 0;

    final IntPredicate positive = v -> v > 0;
    final Index positiveIndex = new Index(positive);
    final Index negativeIndex = new Index(positive.negate());

    while (positiveIndex.index < array.length || negativeIndex.index < array.length) {
      sum += sum < 0 ? sum(positiveIndex, negativeIndex) : sum(negativeIndex, positiveIndex);
    }

    return sum;
  }

  private int sum(@NotNull Index mainIndex, @NotNull Index secondaryIndex) {
    int localSum = 0;
    // searching for the next suitable element
    while (mainIndex.index < array.length && secondaryIndex.sign.test(array[mainIndex.index])) {
      mainIndex.index++;
    }

    if (mainIndex.index < array.length) {
      localSum += array[mainIndex.index++];
    } else {
      // add the remaining elements
      for (; secondaryIndex.index < array.length; secondaryIndex.index++) {
        if (secondaryIndex.sign.test(array[secondaryIndex.index])) {
          localSum += array[secondaryIndex.index];
        }
      }
    }
    return localSum;
  }

  private static final class Index {
    @NotNull
    private final IntPredicate sign;

    private int index;

    public Index(@NotNull IntPredicate sign) {
      this.sign = sign;
    }
  }
}

答案 2 :(得分:0)

选项1: 对数组进行就地排序并迭代一半以上。在每个步骤中,使用i元素添加size-i-1元素。

如果有一些大数字但很多小的负数(反之亦然),则不起作用。

选项2(改进):

就地排序。

保留两个索引 - 一个在开头,一个在结尾。他们见面时退出循环。在每一步,如果到目前为止的结果为负,则在第二个索引处添加值并使其前进。如果结果为正,则在第一个索引处添加值并将其前进。