简体   繁体   English

打印出一个递归等于给定和的数组中的所有子集

[英]Print out all subsets in an array that equal an given sum recursively

I have a weird homework that I have to write a program with a method that takes an array of non-negative integers (array elements can have repeated values) and a value sum as parameters. 我有一个奇怪的作业,我必须用一个方法编写一个程序,该方法采用非负整数数组(数组元素可以有重复值)和值和作为参数。 The method then prints out all the combinations of the elements in array whose sum is equal to sum. 然后该方法打印出数组中元素的所有组合,其总和等于和。 The weird part is, the teacher forces us to strictly follow the below structure: 奇怪的是,老师强迫我们严格遵循以下结构:

public class Combinations {

    public static void printCombinations(int[] arr, int sum) {
       // Body of the method
    }

    public static void main(String[] args) {
       // Create 2-3 arrays of integers and 2-3 sums here then call the above
       // method with these arrays and sums to test the correctness of your method
    }

} 

We are not allow to add neither more methods nor more parameters for the current program. 我们不允许为当前程序添加更多方法或更多参数。 I have researched and understood several ways to do this recursively, but with this restriction, I don't really know how to do it. 我已经研究并了解了几种递归方式,但有了这个限制,我真的不知道怎么做。 Therefore, I appreciate if you guys help me out. 因此,如果你们帮助我,我感激不尽。

EDIT: The array can have repeated elements. 编辑:数组可以有重复的元素。 Here's an example run of the program. 这是程序的示例运行。

arr = {1, 3, 2, 2, 25} and sum = 3

Outputs: 输出:

(1, 2) // 1st and 3rd element (1,2)//第1和第3个元素

(1, 2) // 1st and 4th element (1,2)//第1和第4个元素

(3) // 2nd element (3)//第二元素

As the printCombinations() method accepts the integer array as parameter and you are not allowed to add any additional methods. 由于printCombinations()方法接受整数数组作为参数,因此不允许添加任何其他方法。 I couldn't think of Recursion without adding an additional method. 没有添加额外的方法我想不到递归。

Here is a solution, let me know if this helps. 这是一个解决方案,让我知道这是否有帮助。 And this is not the best way! 这不是最好的方法!

public static void main( String[] args ) throws Exception {
    int arr[] = {1, 3, 2, 2, 25, 1, 1};
    int sum = 8;
    printCombinations(arr, sum);
}

public static void printCombinations(int arr[], int sum){
    int count = 0;
    int actualSum = sum;
    while (count < arr.length) {
        int j = 0;
        int arrCollection[] = new int[arr.length];
        for (int k = 0; k < arrCollection.length; k++){
            arrCollection[k] = -99; // as the array can contain only +ve integers
        }
        for (int i = count; i < arr.length; i++) {
            sum = sum - arr[i];
            if (sum < 0){
                sum = sum + arr[i];
            } else if (sum > 0){
                arrCollection[j++] = arr[i];
            } else if (sum == 0){
                System.out.println("");
                arrCollection[j++] = arr[i];
                int countElements = 0;
                for (int k = 0; k < arrCollection.length; k++){
                    if (arrCollection[k] != -99) {
                        countElements++;
                        System.out.print(arrCollection[k] + " ");
                    }
                }
                if (countElements == 1){
                    i = arr.length -1;
                }
                sum = sum + arr[i];
                j--;
            }
        }
        count++;
        sum = actualSum;
    }
}

This is extremely suited for recursive algorithm. 这非常适合递归算法。

Think about function, let's call it fillRemaining , that gets the current state of affairs in parameters. 考虑一下函数,我们称之为fillRemaining ,它获取参数中的当前事务状态。 For example, usedItems would be a list that holds the items that were already used, availableItems would be a list that holds the items that haven't been tried, currentSum would be the sum of usedItems and goal would be the sum you are searching for. 例如, usedItems将是一个包含已经使用的项目的列表, availableItems将是一个包含尚未尝试的项目的列表, currentSum将是usedItems的总和, goal将是您要搜索的总和。

Then, in each call of fillRemaining , you just have to walk over availableItems and check each one of them. 然后,在每次fillRemaining调用中,您只需要遍历availableItems并检查它们中的每一个。 If currentSum + item == goal , you have found a solution. 如果currentSum + item == goal ,您已找到解决方案。 If currentSum + item > goal , you skip the item because it's too large. 如果currentSum + item > goal ,则跳过该项,因为它太大了。 If currentSum + item < goal , you add item to usedItems and remove it from availableItems , and call fillRemaining again. 如果currentSum + item < goal ,则将item添加到usedItems并将其从availableItems删除,然后再次调用fillRemaining Of course, in this call currentSum should also be increased by item . 当然,在这个调用中, currentSum也应该按item增加。

So in printCombinations , you initialize availableItems to contain all elements of arr , and usedItems to empty list. 因此,在printCombinations ,初始化availableItems以包含arr所有元素,并将usedItems用于清空列表。 You set currentSum to 0 and goal to sum , and call fillRemaining . currentSum设置为0并将goal设为sum ,并调用fillRemaining It should do the magic. 它应该做的魔术。

With the restriction of not being able to add any other methods or parameters, you can also make fields for availableItems , usedItems , currentSum and goal . 由于无法添加任何其他方法或参数的限制,您还可以为availableItemsusedItemscurrentSumgoal创建字段 This way, you don't have to pass them as parameters, but you can still use them. 这样,您不必将它们作为参数传递,但您仍然可以使用它们。 The fields will have to be static, and you would set them in main as described above. 字段必须是静态的,您可以将它们设置为main ,如上所述。

If neither adding fields is allowed, then you have to somehow simulate nested loops with variable depth. 如果既不允许添加字段,那么您必须以某种方式模拟具有可变深度的嵌套循环。 In effect, this simulates what would otherwise be passed via stack, but the algorithm is still the same. 实际上,这模拟了否则将通过堆栈传递的内容,但算法仍然是相同的。

In effect, this algorithm would do a depth-first search of (pruned) tree of all possible combinations. 实际上,该算法将对所有可能组合的(修剪)树进行深度优先搜索。 Beware however, that there are 2^n combinations, so the time complexity is also O(2^n). 但要注意,有2 ^ n个组合,因此时间复杂度也是O(2 ^ n)。

I think that all algorithms which can be solved with recursion can also be solved with stacks instead of recursion (see solution below). 我认为所有可以通过递归求解的算法也可以用堆栈而不是递归来解决(参见下面的解决方案)。 But very often it is easier to solve the problems with recursion before attempting the stack based solutions. 但是,在尝试基于堆栈的解决方案之前,通常更容易解决递归问题。

My recursive take on this problems would be in Java something like this: 我对这个问题的递归看法将是这样的:

public static void printCombinations(int[] array, int pos, int sum, int[] acc) {
    if (Arrays.stream(acc).sum() == sum) {
        System.out.println(Arrays.toString(acc));
    }
    for (int i = pos + 1; i < array.length; i++) {
        int[] newAcc = new int[acc.length + 1];
        System.arraycopy(acc, 0, newAcc, 0, acc.length);
        newAcc[acc.length] = array[i];
        printCombinations(array, i, sum, newAcc);
    }
}

This function you can call like this: 你可以这样调用这个函数:

printCombinations(new int[]{1, 3, 2, 2, 25}, -1, 3, new int[]{});

And it will print this: 它会打印出来:

[1, 2]
[1, 2]
[3]

Basically it goes through all possible sets in this array and then filters those out which have the sum of 3 in this case. 基本上它遍历此数组中的所有可能的集合,然后过滤那些在这种情况下总和为3的集合。 It is not great, there are for sure better, more efficient ways to do this. 这不是很好,确实有更好,更有效的方法来做到这一点。 But my point here is simply to show that you can convert this algorithm to a stack based implementation. 但我的观点仅仅是为了表明您可以将此算法转换为基于堆栈的实现。

Here it goes how you can implement the same algorithm using stacks instead of recursion: 这里介绍了如何使用堆栈而不是递归来实现相同的算法:

public static void printCombinationsStack(int[] array, int sum) {
    Stack<Integer> stack = new Stack<>();
    stack.push(0);
    while (true) {
        int i = stack.peek();
        if (i == array.length - 1) {
            stack.pop();
            if (stack.isEmpty()) {
                break;
            }
            int last = stack.pop();
            stack.push(last + 1);
        } else {
            stack.push(i + 1);
        }
        if (stack.stream().map(e -> array[e]).mapToInt(Integer::intValue).sum() == sum) {
            System.out.println(stack.stream().map(e -> Integer.toString(array[e]))
                    .collect(Collectors.joining(",")));
        }
    }
}

This method can be called like this: 可以像这样调用此方法:

printCombinationsStack(new int[]{1, 3, 2, 2, 25}, 3);

And it outputs also: 它还输出:

1,2
1,2
3

How I came to this conversion of a recursive to a stack based algorithm: 我是如何将递归转换为基于堆栈的算法的:

If you observe the positions in the acc array on the first algorithm above, then you will see a pattern which can be emulated by a stack. 如果您在上面的第一个算法中观察到acc数组中的位置,那么您将看到一个可以由堆栈模拟的模式。 If you have an initial array with 4 elements, then the positions which are in the acc array are always these: 如果你有一个包含4个元素的初始数组,那么acc数组中的位置总是如下:

[]
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 3]
[0, 2]
[0, 2, 3]
[0, 3]
[1]
[1, 2]
[1, 2, 3]
[1, 3]
[2]
[2, 3]
[3]

There is a pattern here which can easily be emulated with stacks: 这里有一个模式可以很容易地用堆栈模拟:

The default operation is always to push into the stack, unless you reach the last position in the array. 除非您到达阵列中的最后一个位置,否则默认操作始终是推入堆栈。 You push first 0 which is the first position in the array. 你先推0,这是数组中的第一个位置。 When you reach the last position of the array, you pop once from the array and then pop again and a second popped item which you push back to the stack - incremented by one. 当你到达数组的最后一个位置时,你从数组中弹出一次,然后再次弹出,然后弹出第二个弹出的项目,你将其推回到堆栈 - 增加1。

If the stack is empty you break the loop. 如果堆栈为空,则打破循环。 You have gone through all possible combinations. 您已经完成了所有可能的组合。

似乎重复,请通过以下链接获取正确的解决方案,具有确切的代码复杂性详细信息find-a-pair-of-elements-from-an-array-which-sum-equals-a-given-number

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 递归打印数组中等于给定总和的所有子集 - Print all subsets in an array that equal to a given sum recursively 递归打印出等于给定总和的数组中的所有子集不会跳转到下一个迭代 - Print out all subsets in an array that equal an given sum recursively does not jump to the next iteration 以递归方式打印数组的所有子集 - Print all subsets of an array recursively 动态编程-打印给定总和的所有子集 - dynamic programming - print all subsets with the given sum 给定一个整数数组和一个总和,任务是找出给定数组的子集是否存在总和等于给定总和的子集 - Given an array of integers and a sum, the task is to find if there exists a subsets of given array with sum equal to given sum 使用数组中的前&#39;n&#39;个整数递归打印所有子集 - Recursively Print All Subsets Using First 'n' Integers in an Array 打印数组的所有子集 - Print all subsets of an array 给定一个数组,找到总和为值k的所有子集 - Given an array, find all subsets which sum to value k 回溯 - 给定一组数字,找到总和等于 M 的所有子集(给定 M) - Backtracking - Given a set of numbers, find all the subsets with a sum equal to M (M is given) 递归确定一组数字是否包含总和相等的两个子集 - Recursively determine if a set of numbers contains two subsets that equal in sum
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM