Java递归问题

时间:2010-12-16 12:05:20

标签: java algorithm recursion

我在Java中遇到了一个递归面试问题,需要你的帮助。

写一个**Java function**,使得::给定一个整数数组,是否可以将整数分成两组,这样两组的总和是相同的,具有这些约束:所有的值5的倍数必须在一个组中,并且所有3的倍数(而不是5的倍数)必须在另一个组中。 (不需要循环。)

split53({1,1}) → true
split53({1, 1, 1}) → false
split53({2, 4, 2}) → true

PS:这是hewlett packard的采访问题

5 个答案:

答案 0 :(得分:4)

这个问题很容易简化为:给定一组整数numbers和一个整数target,是否有可能找到numbers的子集,其总和等于{{ 1}}?
如果过渡需要澄清,请告诉我。

可以在target时间内使用DP来解决。我的想法是

  1. O(numbers.size * target)numbers.size时,唯一可达的总和为0
  2. 假设我们有0,在这种情况下,总和numbers == {1, 3}可用。如果我们向{0, 1, 3, 4}numbers添加其他元素会怎样?现在,仍然可以达到所有旧的金额,也可以达到一些新的金额:4。因此,对于{0 + 4, 1 + 4, 3 + 4, 4 + 4},可用的总和为numbers == {1, 3, 4}
  3. 以这种方式,按编号添加数字,您可以构建可达总和的列表。
  4. 一个工作示例(它不处理负数,但你可以轻松解决这个问题)

    {0, 1, 3, 4, 5, 7, 8}

    运行它

    boolean splittable(int[] numbers, int target) {
        boolean[] reached = new boolean[target + 1];
        reached[0] = true;
    
        for (int number : numbers) {
            for (int sum = target - 1; sum >= 0; --sum) {
                if (reached[sum] && sum + number <= target) {
                    reached[sum + number] = true;
                }
            }
        }
    
        return reached[target];
    }
    

    修改
    我刚刚注意到要求的“递归”部分。好吧,如果这是硬性要求,DP可以用memoization重写为递归。这将保留运行时的复杂性。

    编辑2
    在团体。在继续算法之前,必须将可被3或5整除的元素分配给各个组。假设所有元素的总和为System.out.println(splittable(new int[]{3, 1, 4}, 7)); // => true System.out.println(splittable(new int[]{3, 1, 4}, 6)); // => false ,可被3整除的元素之和为s,可被5整除但不是3的元素之和为s3。在这种情况下,在您分配了这些“特殊”元素后,您必须将其余部分拆分为一个组中的总和为s5,另一组s/2 - s3

答案 1 :(得分:1)

非常慢,但工作解决方案:

static boolean canSplit(int[] arr, int lvl, int sum1, int sum2) {
        if (arr.length == lvl) {
            if (sum1 == sum2) {
                return true;
            } else {
                return false;
            }
        }
        if (arr[lvl] % 5 == 0) {
            return canSplit(arr, lvl + 1, sum1 + arr[lvl], sum2);
        } else if (arr[lvl] % 3 == 0) {
            return canSplit(arr, lvl + 1, sum1, sum2 + arr[lvl]);
        }
        return canSplit(arr, lvl + 1, sum1 + arr[lvl], sum2) ||
               canSplit(arr, lvl + 1, sum1, sum2 + arr[lvl]);
    }

调用该函数:

canSplit(arr, 0, 0, 0);

答案 2 :(得分:1)

这是真正的递归解决方案。

private boolean split2(int index, int[] nums, int sum1, int sum2) {
  if (index >= nums.length) {
    return sum1 == sum2;
  }

  if (split2(index + 1, nums, sum1 + nums[index], sum2)) {
    return true;
  }
  if (split2(index + 1, nums, sum1, sum2 + nums[index])) {
    return true;
  }

  return false; 
}

此代码将每个元素放入组中。如果在任何组合中两个组相等,则返回true。没有使用循环,只有一个函数。

最适合所有人

编辑:你的函数需要4个参数作为输入,而问题仅作为数组输入。您必须指定使用您的呼叫split2(0,nums,0,0);

来完成所需的功能

答案 3 :(得分:1)

可能让我被解雇的代码。但是会起作用:D

完全递归,同样致命。

public boolean split53(int[] nums) {
    return split_fn(0, nums, 0, 0, false, false);
}
public boolean split_fn(int start, int[] nums, int left, int right, boolean fiveLeft, boolean chosen) {
    if (start >= nums.length) {
        if (left == right) return true;
        return false;
    }
    if (nums[start] % 5 == 0) {
        if (!chosen) {
            return split_fn(start + 1, nums, left + nums[start], right, true, true) || split_fn(start + 1, nums, left, right + nums[start], false, true);
        } else {
            return split_fn(start + 1, nums, left + ((fiveLeft) ? nums[start] : 0), right + ((!fiveLeft) ? nums[start] : 0), fiveLeft, chosen);
        }

    }

    if (nums[start] % 3 == 0 && nums[start] % 5 != 0) {
        if (!chosen) {
            return split_fn(start + 1, nums, left + nums[start], right, false, true) || split_fn(start + 1, nums, left, right + nums[start], true, true);
        } else {
            return split_fn(start + 1, nums, left + ((!fiveLeft) ? nums[start] : 0), right + ((fiveLeft) ? nums[start] : 0), fiveLeft, chosen);
        }
    }
    //standard case
    return split_fn(start + 1, nums, left + nums[start], right, fiveLeft, chosen) || split_fn(start + 1, nums, left, right + nums[start], fiveLeft, chosen);

}

答案 4 :(得分:0)

我不知道以下解决方案有多快或多慢。但正是它是一种递归回溯解决方案,它没有使用问题中提到的循环。

以下是代码段:

public boolean split53(int[] nums) {
    int start = 0, firstPart = 0, secondPart = 0;
    if (split(start, nums, firstPart, secondPart)) {
        return true;
    }
    return false;
}

public boolean split(int start, int[] nums, int firstPart, int secondPart) {
    if (start >= nums.length) {
        return (firstPart == secondPart);
    }
    if ((start + 1) < nums.length
            && (nums[start] % 3 != 0)
            && (nums[start + 1] % 5 != 0)
            && split(start + 2, nums, firstPart + nums[start], secondPart
                    + nums[start + 1])) {
        return true;
    }
    if ((start + 1) < nums.length
            && (nums[start + 1] % 3 != 0)
            && (nums[start] % 5 != 0)
            && split(start + 2, nums, firstPart + nums[start + 1],
                    secondPart + nums[start])) {
        return true;
    }
    if ((nums[start] % 3 != 0)
            && split(start + 1, nums, firstPart + nums[start], secondPart)) {
        return true;
    }
    if ((nums[start] % 5 != 0)
            && split(start + 1, nums, firstPart, secondPart + nums[start])) {
        return true;
    }
    return false;
}

希望它有所帮助。