【问题标题】:Iterating Java Arrays in Java 8在 Java 8 中迭代 Java 数组
【发布时间】:2017-10-29 17:26:02
【问题描述】:

这是一个简单的算法问题,

给定一个n>1的整数数组,nums,返回一个数组输出 使得 output[i] 等于所有元素的乘积 除 nums[i] 之外的 nums。在 O(n) 中解决它而不用除法。为了 例如,给定 [1,2,3,4],返回 [24,12,8,6]。

这是我的解决方案,

public static int[] productExceptSelf(int[] nums) {

    int[] result = new int[nums.length];

    int leftProduct = 1;
    int rightProduct = 1;

    for(int i = 0; i < result.length; i++){
        result[i] = leftProduct;
        leftProduct *= nums[i];
    }

    for(int i=nums.length -1; i >= 0; i --){
        result[i] *= rightProduct;
        rightProduct *= nums[i];
    }
    return result;
}

public static void main(String[] args) {
    int[] output = productExceptSelf(new int[]{1, 2, 3, 4});
    Arrays.stream(output).forEach(System.out::println);
}

这很好用。我想要学习的是如何在 Java 8 中重写这段代码。就像 Java 8 中循环的不同选项一样。

【问题讨论】:

  • 以上代码在 Java 8 中完全有效。
  • @JoeC 我有点知道,我的意思是如何使用 Java 8 函数构造重写代码。
  • 老实说,这个特殊的例子是我不会尝试这样做的。这主要是因为您在循环中操作多个变量,而 Stream API 的设计不是很好。
  • 流不太适合(就像现在一样)让临时结果流入进一步的流,恕我直言 - 对每个人来说都是一种不幸的浪费时间。。提示:System.out.println(Arrays.toString(output)); 更好看。

标签: arrays java-8 java-stream


【解决方案1】:

你可以用几行不同的代码来做:

public static int[] productExceptSelf(int[] nums) {
    int all = Arrays.stream(nums).reduce(1, (x, y) -> x * y);
    return IntStream.range(0, nums.length).map(x -> all / nums[x]).toArray();
}

【讨论】:

  • 我真的很喜欢这个解决方案,但要求是:在 O(n) 内不除法地解决它
  • @Flown 我已经对这个问题进行了粗略的阅读,甚至没有注意到:(
【解决方案2】:

几个 cmets 和答案都提到流 API 不适合解决这类问题。我同意;使用数组要容易得多。这是我的做法:

static int[] productExceptSelf(int[] nums) {
    int len = nums.length;

    int[] lefts = new int[len];
    Arrays.setAll(lefts, i -> i == 0 ? 1 : lefts[i-1] * nums[i-1]);

    int[] rights = new int[len]; // reversed
    Arrays.setAll(rights, i -> i == 0 ? 1 : rights[i-1] * nums[len-i]);

    int[] result = new int[len];
    Arrays.setAll(result, i -> lefts[i] * rights[len-i-1]);

    return result;
}

public static void main(String[] args) {
    int[] nums = { 1, 2, 3, 4 };
    System.out.println(Arrays.toString(productExceptSelf(nums)));
}

这在概念上与迭代方法非常相似,但它将完整的中间结果存储在临时数组中,以便利用 Arrays.setAll 操作。这会占用更多空间,但执行的乘法次数相同。

我注意到Arrays.setAll 的规范不保证从左到右的操作,但这种方法需要它。事实上,setAll 基本上是IntStream.range(0, length) 的包装,因此如果担心对setAll 的依赖,您可以自己轻松编写此实用程序。

请注意,这基本上是一个“前缀”或“扫描”操作,如Flown's answer 所示。 (+1) 不幸的是,Arrays.parallelPrefix 的局限性,例如无法指定初始元素,以及无法从右到左扫描(以及缺乏反转原始数组的内置方法)使得这种方法虽然不错,但很不方便。

【讨论】:

    【解决方案3】:

    这可以满足您的要求,但我觉得它需要更多优雅:-D

    public static void main(String[] args) {
        List<Integer> output = productExceptSelf(new int[]{1, 2, 3, 4});
        output.forEach(System.out::println);
    }
    
    public static List<Integer> productExceptSelf(int[] nums) {
        List<Integer> list = Arrays.stream(nums).boxed().collect(Collectors.toList());
        List<Integer> results = new ArrayList<>();
        list.forEach(value -> {
            int x = 1;
            for (Integer i : list) {
                if (value.equals(i)) continue;
                x *= i;
            }
            results.add(x);
        });
        return results;
    }
    

    【讨论】:

    • OP 有一个已经非常好的解决方案。这次你的解决方案似乎没有做同样的事情,第一个元素是 24。
    • @JoopEggen 注意指出错误在哪里?我运行了一些测试用例,它们似乎做得很好。
    • 抱歉!解决方案本身很好,很重要,但不是 O(N),而是 O(N²)。我希望有更多的流媒体。我还是应该这么说的。
    【解决方案4】:

    正如 cmets 中已经说过的:Streams 不太适合这种任务,但是 JDK 中还有其他方法,例如 Arrays::parallelPrefix

    我采纳了你的想法并将其转化为:

    private static int[] productExceptSelf(int[] arr) {
      IntBinaryOperator product = (a, b) -> a * b;
    
      int[] leftPrefix = IntStream.concat(IntStream.of(1), IntStream.of(arr)).toArray();
      Arrays.parallelPrefix(leftPrefix, product);
    
      int[] reversedRightPrefix = reversed(IntStream.concat(IntStream.of(arr), IntStream.of(1))
                                                 .toArray());
      Arrays.parallelPrefix(reversedRightPrefix, product);
      //Could be calculated on-the-fly: reversedRightPrefix[reversedRightPrefix - i - 2]
      int[] rightPrefix = reversed(reversedRightPrefix);
    
      return IntStream.range(0, arr.length)
                     .map(i -> product.applyAsInt(leftPrefix[i], rightPrefix[i + 1]))
                     .toArray();
    }
    
    private static int[] reversed(int[] arr) {
      return IntStream.range(0, arr.length).map(i -> arr[arr.length - 1 - i]).toArray();
    }
    

    由于您要求循环替代方案,您可以使用递归(仍然是 Java 8 代码):

    private static int[] productExceptSelf(int[] arr) {
      int[] result = new int[arr.length];
      productExceptSelf(arr, result, 0, 1);
      return result;
    }
    
    private static int productExceptSelf(int[] arr, int[] result, int i, int leftPrefix) {
      if (arr.length <= i) {
        return 1;
      }
      int rightPrefix = productExceptSelf(arr, result, i + 1, leftPrefix * arr[i]);
      result[i] = leftPrefix * rightPrefix;
      return rightPrefix * arr[i];
    }
    

    【讨论】:

    • 加一个供Arrays.parallelPrefix使用。
    猜你喜欢
    • 2020-08-09
    • 2014-06-09
    • 1970-01-01
    • 2017-08-08
    • 2016-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-29
    相关资源
    最近更新 更多