#494 Target Sum LeetCode question using Recusion with Memoization - java

I came across the following question in LeetCode while studying stacks and I'm able to get the brute-force solution. There's a lot of videos + solutions on the DP method but I would like to understand the recursion with memoization method (since I'm not studying DP).
I thought that by maintaining the sum in calc(), I was keeping the previously computed results, which implies memoization... but I suppose that's not the case.
My current brute-force solution is
class Solution {
private int count = 0;
public int findTargetSumWays(int[] nums, int target) {
calc(nums, target, 0, 0);
return this.count;
}
private int calc(int[] nums, int target, int sum, int i) {
if (i == nums.length) {
if (sum == target) {
count++;
}
return sum;
}
calc(nums, target, sum + nums[i], i+1);
calc(nums, target, sum - nums[i], i+1);
return 0;
}
}
The problem statement is:
You are given an integer array nums and an integer target.
You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers.
For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression "+2-1".
Return the number of different expressions that you can build, which evaluates to target.

This solution is Targer Sum problem from leetcode with recursive aaproach
public int findTargetSumWays(int[] nums, int target) {
return countTarget(nums, 0, 0, target);
}
public int countTarget(int[] nums, int pos, int sum, int target) {
if (nums.length == pos) {
return sum == target ? 1 : 0;
}
return countTarget(nums, pos + 1, sum + -nums[pos], target)
+ countTarget(nums, pos + 1, sum + nums[pos], target);
}

Related

Use Dynamic Programming (memoization) to get this function below 2^n time complexity

I have a recursive algorithm that I wrote, which splits an array and finds the minimum of the differences between the two resulting arrays. For example:
If the input array was: [1,2,2,3,3] the minimum difference would be 1. ((1+2+2) - (3+3)).
If the input array was: [1,100,4,2] the minimum difference would be 93. (100 - (1+4+2)).
My algorithm is O(2^n) time complexity. I'm trying to use DP with memoization to lower the runtime.
The best that I've achieved is O(2^n / 2) using this memo that prunes half of the recursions, in the worst case. Worst case is that all sums for each level are unique.
How can I do better?
public static int minDiffRecursive(int[] nums) {
int sum = 0;
for(int num : nums) {
sum += num;
}
int[][] memo = new int[nums.length][sum];
for(int[] row : memo) {
Arrays.fill(row, -1);
}
return move(nums, 0, 0, 0, memo);
}
private static int move(int[] nums, int i, int sum1, int sum2, int[][] memo) {
if(i >= nums.length) return Math.abs(sum1 - sum2); //if at the end, return difference
if(memo[i][Math.min(sum1, sum2)] > -1) return memo[i][Math.min(sum1, sum2)]; //take the min of sum1 and sum2, which are mirror images for each i. Return stored value if exists.
int add1 = move(nums, i + 1, sum1 + nums[i], sum2, memo); //check path adding item to sum1
int add2 = move(nums, i + 1, sum1, sum2 + nums[i], memo); //check path adding item to sum2
return memo[i][Math.min(sum1, sum2)] = Math.min(add1, add2); //return the minimum of the two paths
}

How to solve a partition problem using recursion only

I got a partition problem for which I need advice. I'm given a 1D array whose length is even. I need to write a boolean method to determine whether the array can be divided into 2 equally sized subarrays with equal sum, no loops are allowed.
For example, array #1 {-3,5,-12,14,-9,13} will return false, because -3 + 5 -12 + 14 = -9 + 13, however on the left side there are 4 elements and on the other side 2.
array #2 will return true:
{-3,14,12,5,-9,13} : -3 + 5 + 14 = 12 - 9 + 13
Here's what I've done so far:
public static boolean canBeDividedEqually(int[] arr)
{
if (arr.length %2 ==0){
return canBeDividedEquallyHelper(arr, 0, 0, 0);
}
return false;
}
public static boolean canBeDividedEquallyHelper(int[] arr, int i, int sum1, int sum2)
{
if (i == arr.length){
return sum1 == sum2;}
return canBeDividedEquallyHelper(arr, i+1, sum1 + arr[i], sum2) ||
canBeDividedEquallyHelper(arr, i+1, sum1, sum2 + arr[i]);
}
For case #2, it will return true as expected, but for case #1 it will also return true. I need to add a condition that will disqualify an array of type case #1.
You were almost there. In addition to the sums, pass the number of elements:
public class Solver
{
public static boolean canBeDividedEqually(int[] arr)
{
return canBeDividedEquallyHelper(arr, 0, 0, 0, 0, 0);
}
public static boolean canBeDividedEquallyHelper(int[] arr, int i, int nb1, int sum1, int nb2, int sum2)
{
if (i == arr.length)
return nb1 == nb2 && sum1 == sum2;
return canBeDividedEquallyHelper(arr, i+1, nb1+1, sum1 + arr[i], nb2, sum2) ||
canBeDividedEquallyHelper(arr, i+1, nb1, sum1, nb2+1, sum2 + arr[i]);
}
public static void main(String[] args)
{
System.out.println(canBeDividedEqually(new int[]{-3, 5, -12, 14, -9, 13})); // false
System.out.println(canBeDividedEqually(new int[]{-3, 14, 12, 5, -9, 13})); // true
}
}
Try this.
static boolean canPartitioning(int[] arr) {
return new Object() {
int length = arr.length, half = length / 2;
boolean partition(int i, int selected, int sum, int rest) {
if (i >= length)
return selected == half && sum == rest;
return selected < half && partition(i + 1, selected + 1, sum + arr[i], rest)
|| partition(i + 1, selected, sum, rest + arr[i]);
}
}.partition(0, 0, 0, 0);
}
public static void main(String[] args) {
System.out.println(canPartitioning(new int[] {-3, 5, -12, 14, -9, 13}));
System.out.println(canPartitioning(new int[] {-3, 14, 12, 5, -9, 13}));
}
output:
false
true
Here's a solution without using loops,
static int[] arrA, arrB;
public static boolean equalSplit(int[] arr) {
//Step 1
if (arr.length % 2 == 0) {
int sumA = 0, sumB = 0; // Two int variables to store the value of sum.
// Initializing the two new arrays with equal length.
arrA = new int[arr.length / 2];
arrB = new int[arr.length / 2];
// Copying the elements from the given array to the new arrays.
arrA = createArray(arrA, 0, arr, 0);
arrB = createArray(arrB, 0, arr, arr.length / 2);
//Calculating and storing the sum in the variables.
sumA = arraySum(arrA, arrA.length);
sumB = arraySum(arrB, arrB.length);
return sumA == sumB; // Checking the codition
} else {
return false;
}
}
private static int[] createArray(int[] arr, int index, int[] copyFrom, int copyFromIndex) {
if(index == arr.length) return arr;
arr[index] = copyFrom[copyFromIndex];
index++;
copyFromIndex++;
return createArray(arr, index, copyFrom, copyFromIndex);
}
private static int arraySum(int[] arr, int N) {
if (N <= 0) return 0;
return (arraySum(arr, N - 1) + arr[N - 1]);
}
My approach towards this question is,
Step 1 -> Checking whether can you split the given array into two equal arrays. If yes next comes the Step 2 or return false without any further steps.
Step 2 -> Copying the given array elements into two different but equal arrays using recursion.
Step 3 -> Sum the newly populated two arrays and store it in two different variable.
Step 4 -> If the Sum of both the newly populated arrays is equal then the function return true else false.
Explanation :
Create two new integer arrays which are going to get populated only if the given array can be divided into two equal parts. Here it is arrA and arrB.
Check whether if the length of given array can be divided by two and have 0 remainders because this can give the answer to the question "Can this array be divided into two equal parts ?". The piece of code in this answer is arr.length % 2 == 0. If the given array satisfies this condition only then the steps given below will be carried out else it will return false.
Initialize two integer variable to store the value of Sum of both the equally divided arrays.
Initialize the two newly created arrays with the array length of half the given array which is arr.length / 2.
Then copy first half of given array elements to the first newly initialized array then the second half to the second array. To achieve this createArray(int[] arr, int index, int[] copyFrom, int copyFromIndex) method is used. arr is the argument to pass the array which should be copied to, index should be 0 because it is used as the index of newly created arrays, copyFrom is a parameter for the given array which has all the elements, copyFromIndex is used to start copying the elements from the given index.
Then calculate the sum using recursive function and store it in separate variable which was created earlier. The function used here was arraySum(int[] arr, int N).
Return whether both the sum variables are equal.
If the change of elements order is allowed, you have to check every possible permutation of the array:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] a = {-3, 5, -12,14,-9,13};
int[] b = {-3,14, 12, 5,-9,13};
System.out.println(isEquallySplittable(a));
System.out.println(isEquallySplittable(b));
}
public static boolean isEquallySplittable(int[] array){
long arraySum = arraySum(array);
if(arraySum % 2 != 0) return false; //can not split an even sum by half
return isEquallySplittable(array, 0, 0, arraySum);
}
public static boolean isEquallySplittable(int[] array, int indexFrom, int indexTo, long sumArray){
if(indexTo == indexFrom) {
indexTo++;
}
if(indexTo >= array.length) {
indexTo = 0;
indexFrom++;
}
if(indexFrom >= array.length) return false;
long sumHalfArray = arraySum(Arrays.copyOf(array, array.length/2)); //sum first half of the array
if(sumArray == sumHalfArray * 2 ){
System.out.println(Arrays.toString(array) + " can be split into 2 equal arrays");
return true;
}
//next permutation
int temp = array[indexTo];
array[indexTo] = array[indexFrom];
array[indexFrom] = temp;
return isEquallySplittable(array, indexFrom, ++indexTo, sumArray);
}
public static long arraySum(int[] arr) {
return arraySum(arr, 0, 0);
}
private static long arraySum(int[] arr, int index, long sum) {
if (index >= arr.length ) return sum;
sum += arr[index++];
return arraySum(arr,index,sum);
}
}
Here is Recursive solution in which elements need not be consecutive. This is simple recursive approach in which we take two lists named list1 and list2 respectively.
Then we check if it is possible to obtain a solution by adding this element in list1 (means nums[index] in context of program) if not it will check by adding the same in list2.
When the condition being true the the function recursively add element to respective list and if no solution is possible then function returns false.
The time complexity of the solution is O(2^n) as function tries each possible combination of elements adding to lists.
Here is code:
Example: int nums[] = new int[]{1, 1, 3, 4, 7};
For this resulting output would be [4, 3, 1] & [7, 1] order of elements get changed due to recursive calls.
import java.util.ArrayList;
public class SumPartition {
/**
* These are list that stores possible sum arrays
*/
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
public static void main(String[] args) {
int[] arr = new int[]{1, 1, 3, 4, 7};
SumPartition sumPartition = new SumPartition();
if(sumPartition.sameSumArrays(arr, 0, 0, 0)) {
System.out.println(sumPartition.list1);
System.out.println(sumPartition.list2);
}
else {
System.out.println("This is not possible");
}
}
/**
*
* #param nums is array of numbers
* #param index is index of nums array
* #param sum1 is sum1 which is sum of elements in list1
* #param sum2 is sum2 which is sum of elements in list2
*/
public boolean sameSumArrays(int nums[], int index, int sum1, int sum2) {
if(index == nums.length) {
return sum1 == sum2;
}
if(sameSumArrays(nums, index+1, sum1+nums[index], sum2)) {
list1.add(nums[index]);
return true;
}
else if(sameSumArrays(nums, index+1, sum1, sum2+nums[index])) {
list2.add(nums[index]);
return true;
}
return false;
}
}

How to recursively count number of occurences in an array

I've got a task that gets an int value "n" and an Int Array as parameters and is supposed to return a boolean.
The method is supposed to determine, how many "n" are in the given Array. If the number is even the method should return true, else false. If the Array has the length 0, it should return "false" aswell.
What i managed to do is :
public static boolean evenNumberOf(int n, int[] arr) {
boolean result = false;
System.out.println("Starting count");
if (n < arr.length) {
if (arr[n] == n) {
result = true;
} else {
return evenNumberOf(n - 1, arr);
}
}
return result;
}
Im just really confused and i dont know what to do to be honest. I have really tried my best but the longer i work on this task the less i understand.
Any help is appreciated and thank you in advance! :)
Separate it into two methods:
The method you call initially
and a method that gets called recursively to count the number of ns in the array:
boolean evenNumberOf(int n, int[] arr) {
int count = countNs(n, arr, 0);
// Logic to choose what to return based on count and/or length of arr.
}
int countNs(int n, int[] arr, int i) {
// Check if arr[i] is equal to n.
// Make a recursive call to countNs for i := i + 1.
// Combine the check/recursive call result to return a value.
}
Try
//arr should not be empty, index and count >= 0
public static boolean evenNumberOf(int value, int index,int[]arr, int count) {
if(index >= arr.length) return count%2 == 0;
if(arr[index] == value ) {
count++;
}
return evenNumberOf(value, ++index, arr, count);
}
Usage example: System.out.println(evenNumberOf(2, 0, new int[]{2,0,3,7,6,11,1,2}, 0));
(You can add an helper method evenNumberOf(int value,int[]arr))
as Recursive Counting in an Array got closed as a duplicate I will answer it here:
Let's analyze what you did and why it's wrong
public static int countN(int n,int [] arr,int i, int count) {
if (arr[i] == n) {
System.out.println("MATCH");
count++;
return count;
}
Here you already return the count when you get a match. You shouldn't do that because if the first number is already the same it returns 1. all you need to do is increase the count here
else {
System.out.println("Moving on");
i = i + 1;
countN(n,arr,i, count);
}
Here you do the recursion. This is good. But this also needs to be done in the case that you do get a match. And it needs to return that value. But, also this only needs to be done when you are not at the end of the array yet
if (arr.length == i) {
evenNumberOf(n,arr);
}
this part doesn't make sense, because you call evenNumberOf with the exact same arguments as it started so it will result in an infinite loop. you should have returned the count here. also keep in mind that the last index of an array is length - 1
putting this together you can make:
public static int countN(int n,int [] arr,int i, int count) {
if (arr[i] == n) {
count++;
}
if (arr.length - 1 == i) {
return count;
}
return countN(n, arr, i + 1, count);
}

Majority element array Java O(nlogn)

The majority element in an array of size n is an element that appears more than n/2 times. I have to write a function that returns the majority element(if there is one, else return -1) and it must be O(nlogn). This is what I got:
public class MyMajority implements Majority {
public int findMajority(Sequence numbers) {
if (numbers.size()==0) {
return -1;
}
return major(numbers,0,numbers.size()-1);
}
public int major(Sequence numbers, int low, int high) {
if (low == high){
return numbers.get(low);
}
int mid = (high - low) / 2 + low;
int left_major = major(numbers, low, mid);
int right_major = major(numbers, mid + 1, high);
if (left_major == right_major){
return left_major;
}
int left_count = getFrequency(numbers, left_major);
int right_count = getFrequency(numbers, right_major);
return left_count > numbers.size() / 2 ? left_major :
(right_count > numbers.size() / 2 ? right_major : -1);
}
public int getFrequency(Sequence numbers, int major) {
int count = 0;
for(int i=0; i<numbers.size(); i++){
if(numbers.get(i)==major){
count++;
if(count> numbers.size()/2){
break;
}
}
}
return count;
}
However if I run the code some testcases say your algorithm is too slow.
But I'm pretty sure this is O(nlogn) am I missing something? Because I use divide and conquer and loop over the array so T(n)=2T(n/2)+O(n)=O(nlogn)
Maybe you can try this approach, which should be O(nlogn):
It's just simple sort and find the majority item. Since the requirement indicated there is always a majority number, so here it did not consider the edge case.
public class Solution {
public int majorityElement(int[] nums) {
int len = nums.length;
Arrays.sort(nums);
return nums[len/2];
}
}

Java method to recursively add each integer in an array to all of the elements following it?

For example if I have an array of ints as a parameter to my method, my method needs to return an array of ints where each element is the sum of all the elements following it.
Example:
parameter is [5, 6, 7, 2, 3, 1] I need to return [24, 19, 13, 6, 4, 1]
I have a written a helper method that correctly adds an index to all of the ones after it here:
public static int sum(int[] array, int index) {
if (index == array.length) {
return array[array.length-1];
} else {
return array[index] + sum(array, index + 1);
}
}
This all works as it should, but I'm having trouble with the original method here:
public int[] reverseCumulative(int[] numbers) {
int[] temp = new int[numbers.length];
if (numbers.length == 0) {
return temp;
}
else {
temp[numbers.length-1] = sum(numbers, numbers.length);
numbers = Arrays.copyOf(numbers, numbers.length - 1);
reverseCumulative(numbers);
return temp;
}
}
The output here is [0, 0, 0, 0 , 0, 1]. I understand that this is most likely due to the fact I'm creating a new int[] temp every time I call the reverseCumulative method within itself, but I am completely lost and any push in the right direction would be appreciated.
Edit: Forgot to add, I am not allowed to use any loops.
Since you are creating a new array each time you call the reverseCumulative method, you should use the sum method as a helper method inside the reverseCumulative method so that you are still using recursion. For example,
public int[] reverseCumulative(int[] numbers) {
int[] temp = new int[numbers.length];
if (numbers.length == 0) {
return temp;
}
else {
for(int i = 0; i < numbers.length; i++){
temp[i] = sum(numbers,i);
}
return temp;
}
}
This way, each element in temp equals a sum of integers in the numbers array depending on which iteration it is currently on. In the first iteration, temp[0] = the sum of all the ints in numbers. In the second iteration, temp[1] = the sum of all the ints in numbers except the first int and so on. However, the way the sum method is written right now, it adds the last element twice so here's a simple fix,
public static int sum(int[] array, int index) {
if (index == array.length-1) {
return array[array.length-1];
} else {
return array[index] + sum(array, index + 1);
}
}
Maybe something like this?
public static int sum(final int[] target, final int[] source, int index) {
if (index >= source.length - 1)
return source[index];
return target[index] = source[index] + sum(target, source, index + 1);
}
public static int[] reverseCulmulative(final int[] array) {
final int[] target = array.clone();
sum(target, array, 0);
return target;
}

Categories