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) // 1st and 4th element
(3) // 2nd element
As the printCombinations() method accepts the integer array as parameter and you are not allowed to add any additional methods. 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. 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.
Then, in each call of fillRemaining
, you just have to walk over availableItems
and check each one of them. If currentSum + item == goal
, you have found a solution. If currentSum + item > goal
, you skip the item because it's too large. If currentSum + item < goal
, you add item
to usedItems
and remove it from availableItems
, and call fillRemaining
again. Of course, in this call currentSum
should also be increased by item
.
So in printCombinations
, you initialize availableItems
to contain all elements of arr
, and usedItems
to empty list. You set currentSum
to 0
and goal
to sum
, and call 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
. 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.
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).
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. 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. If you have an initial array with 4 elements, then the positions which are in the acc
array are always these:
[]
[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. 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.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.