【问题标题】:Find sum of integer array without overflow查找没有溢出的整数数组的总和
【发布时间】:2013-01-27 07:11:58
【问题描述】:

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

这是面试材料中的问题,不是家庭作业

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

【问题讨论】:

  • 只要结果正确,溢出就是问题?如果没有,就求和。
  • @Ingo 有 ;) 最左边的位 0 反转为 1
  • @meze 那么,也许最低有效位是 奇/偶 位?

标签: java algorithm


【解决方案1】:

首先要注意,即使让即时结果溢出,如果能表示出来,最终结果总是正确的。这是因为在大多数语言中,任何大小的整数类型都类似于循环组,包括 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 位。如果是,则声明结果溢出。

【讨论】:

  • 你能给个代码吗,我的意思是你怎么看它是否有超过k位?。
【解决方案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;
    }
  }
}

【讨论】:

    【解决方案3】:

    选项 1: 就地对数组进行排序并迭代其中的一半。在每一步中,将ith 元素与size-i-1th 元素相加。

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

    选项 2(改进):

    就地排序。

    保留两个索引 - 一个在开头,一个在结尾。当他们相遇时退出一个循环。如果到目前为止的结果是负数,则在每一步中添加第二个索引处的值并推进它。如果结果是肯定的,则在第一个索引处添加值并推进它。

    【讨论】:

    • #2 仍然是n log n。我可以做得更好。
    • 我可以在O(n) 时间内对它们进行排序,但如果 K 很大,则会以大量空间为代价...... :)
    • 我可以做到O(n)就地时间
    猜你喜欢
    • 2015-11-01
    • 2010-12-02
    • 2014-11-08
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 2022-01-07
    • 2018-09-23
    • 2016-10-09
    相关资源
    最近更新 更多