【问题标题】:Why am I for this simple task getting the Timed out error even if the arrays are not that big? [closed]即使数组不是那么大,为什么我要为这个简单的任务得到超时错误? [关闭]
【发布时间】:2018-11-07 10:10:12
【问题描述】:

我正在 codility.com 上执行一项任务,但出现超时错误。代码和任务描述如下。

任务文本(数组已初始化):class Solution { public int solution(int[] A); } 给定数组AN 整数,返回在@ 中不出现的最小正整数(大于0) 987654324@.

例如,给定A = [1, 3, 6, 4, 1, 2],函数应该返回5。给定A = [1, 2, 3],函数应该返回4。给定A = [−1, −3],函数应该返回1

为以下假设编写一个有效的算法:N 是 [1..100,000] 范围内的整数;数组A 的每个元素都是 [−1,000,000..1,000,000] 范围内的整数。

class Solution {
    public int solution(int[] A) {
        int k;
        for (int i = 1;; i++) {
            final int j = i;
            if (!Arrays.stream(A).anyMatch(x -> x != j)) {
                k = j;
                break;
            }
        }
        return k;
    }
}

【问题讨论】:

  • 您的问题没有在任何地方说明 A 的内容。所以它不能被其他人复制。
  • 代码应该做什么,你有没有分析过它对一些样本输入做了多少比较?
  • 我认为for (int i = 1;; i++) 会导致死循环
  • @B001ᛦ 但这不一定是问题。还是有办法退出循环的。
  • @luk2302 这取决于 A 的内容...一般来说,对于公共方法来说,依靠输入的有效性来避免死循环是个坏主意。

标签: java collections java-8 java-stream


【解决方案1】:

看起来您的数组 A 不包含任何正数。因此,您的循环不会中断。

O(n log n) 解:

public int solution(int[] A) {
    final int solution[] = {1};
    Arrays.stream(A)
            .filter(i -> i > 0)
            .sorted()
            .forEach(i -> {
                if (i == solution[0]) {
                    solution[0]++;
                }
            });
    return solution[0];
}

O(n)解决方案:

 public int solution2(int[] A) {
    BitSet bitSet = new BitSet();
    Arrays.stream(A)
            .filter(i -> i > 0)
            .forEach(bitSet::set);
    return bitSet.nextClearBit(1);
}

【讨论】:

  • 我曾假设 OP 已按照 Eran 的回答更正了代码,但您是对的。问题还是错了。
  • 对不起。我以为我在发帖前纠正了这一点。
  • @ETO 谢谢。它正在工作。
  • @PeterLawrey 不。在这种特殊情况下,没有区别。正如IntStream.of(int... values) 调用Arrays.stream(values) 一样。请参考静态方法源代码java.util.stream.IntStream.of(int...)
  • @ETO 好点,Arrays.stream() 已超载。这是 Arrays.asList(A).stream()` 不起作用。
【解决方案2】:

你有一个 O(N*m) 的算法,这将是低效的。

我建议使用只传递一次数组的不同策略。 O(n) 例如使用 BitSet 记录存在哪些正数。然后在 BitSet 中找到第一个丢失的条目。例如BitSet.nextBitClear(1)

一个更简单的解决方案是对数组进行排序并找到第一个丢失的元素,但是,这是 O(n ln n),它比较慢但可能足够快。

【讨论】:

  • O(n log n) 并不一定意味着排序解决方案较慢。对于给定的数字和示例尤其如此,即范围 [−1,000,000..1,000,000] 范围内的数字,而是较小的数组。在这里,我会选择排序而不是 BitSet,考虑到最近 JDK 中的实际实现平均比 n log n 执行排序更好......
  • @Holger 鉴于 N 的范围是 100,000,很容易假设他们有一个包含 1 ... 99,999 个条目的数组,或者至少这是我会尝试的。理想情况下,您将根据数组的大小选择两个实现。
  • @Holger 在任何情况下,如果 n 很小,O(n^2) 算法也可以。尤其是如果您确保首先预热代码。例如如果热身效果不佳,放弃使用 Stream 可能会产生更大的影响。
  • 只考虑解必须在数组长度内,或者如果数组包含所有连续数字,则为数组长度加一,因此,位集最多需要数组长度的大小(指定为最多 100,000),而不是 1,000,000,因为我们可以跳过范围之外的所有数字,甚至减少要考虑的数字范围(如 this)。然后,位集可能会优于所有其他解决方案。
  • 好吧,在我的解决方案中,我首先为 BitSet 指定了一个容量,以避免扩展操作,但后来决定反对它,因为确实有可能所需的容量要小得多,或者在整个操作过程中,bitset 甚至保持为空。由于范围的动态适应,这并非不可能。在这些情况下,位集的分配非常便宜,并且不比分配流或任何其他用于临时存储的东西差。并且代码复杂度不高于其他解决方案。
【解决方案3】:

只有当输入数组的所有元素都相同时,您的程序才会终止(在这种情况下,它会返回该元素的值)。

否则,您将陷入无限循环。

Arrays.stream(A).anyMatch(x -> x != j)true 当且仅当数组的任何元素不等于j。因此!Arrays.stream(A).anyMatch(x -> x != j)true 当且仅当数组的所有元素都等于j,只有当数组的所有元素都相等时才会发生这种情况。

如果您将条件更改为!Arrays.stream(A).anyMatch(x -> x == j),您的程序现在会搜索未出现在输入数组中的最小正数int。如果您仍然为此超时,这意味着您应该更有效地实施算法。

不要从 1 迭代到 n 直到找到一个不在数组中的数字(并且在每次迭代中遍历输入数组),您可以在最多两次迭代中找到不在数组中的最小正数 int数组元素(可以这样做,例如,将数组元素放入TreeSet<Integer>,然后遍历Set 的元素,直到找到不在Set 中的最小正数) .

【讨论】:

  • 抱歉我的错插入了 == 我设置了 !=。谢谢你。
  • @JasminJaskoMerušić 你是说超时的代码有if (!Arrays.stream(A).anyMatch(x -> x == j))
  • 这里的问题是我插入了== 我已经放了!=。用那个回答了这个问题。
【解决方案4】:

对于一个有效的解决方案,您应该考虑对于任何解决方案 n,数组必须至少包含所有 n-1 个正数,因此必须至少有长度n-1。这反过来又可以得出结论,解决方案永远不会大于数组长度加一。

所以我们只需要记录小于这个限制的正数。此外,该范围之外的每个值都会减少可用于 n-1 个元素的数组元素的数量,因此,我们可以进一步缩小范围。

As suggested by Peter Lawrey,您可以使用BitSet 记录可能解决方案范围内遇到的值。这个类还有一个高效的内置操作,用于查找第一个清除的位,它匹配最小的未遇到值。

public int solution(int[] a) {
    int limit = a.length;
    BitSet encountered = new BitSet();
    for(int value: a)
        if(value < 1 || value > limit) limit--; else encountered.set(value);
    return encountered.nextClearBit(1);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-25
    • 1970-01-01
    • 2020-12-21
    • 2021-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多