Search code examples
javaarraysrecursionsublist

How would one go about "chopping up" an array using recursion?


How do you create new, smaller arrays out of a main array using recursion in Java? The only way I can think of is to return an array, otherwise it will be out of scope.

I came here with the intention of finding evidence to prove that recursing through an array is as efficient as looping, since both the array and the local array defined in the method point to the same location in memory.

However I came across this answer: https://stackoverflow.com/a/5112821/10807670 which states that you should use the original array, and not a "chopped-up" version, implying that such a thing could happen by accident.

I managed to prove that both the main array and the array defined in the method are the same array, but I'm still not sure how I could create several subarrays out of the original array using recursion.

class Main {
  public static void main(String[] args) {
    int[] list={2,4,6,8,10};
    System.out.println(list[0]);
    System.out.println(list[1]);
    change(list, 0);
    System.out.println(list[0]);
    System.out.println(list[1]);
  }

  public static void change(int[] list1, int index){
      //does a recursive call initialize a new array or does it point to the same array in memory?

    list1[index]=1; //set all indices to 1

    if (index==list1.length-1) return;

    else change(list1, index+1);
    return;
    }
}

The original response weirdly made me think that every time index was incremented, the change method would be called with a smaller array that started one index after, which I guess is true but only conceptually, as those indices are still there, just not being accessed.

Instead, when I change the array list1 it also changes the array list, as I expected since they are the same array. Is there any way to "chop it up" as the original post implies, let's say, to make a series of new arrays like {4,6,8,10}, {6,8,10}, {8,10}, etc.?


Solution

  • Processing arrays recursively can be implemented in different forms. To stay practical, I will compare two common ways in terms of memory usage.

    Suppose we want to find the sum of array elements. Let's consider two ways:

    • On each recursive step - pass next index to process:
    int sum(int[] nums) {
      return sum(0, nums);
    }
    
    int sum(int index, int[] nums) {
      if (index == nums.length) {
        return 0;
      } else {
        return nums[index] + sum(index + 1, nums);
      }
    }
    
    • On each recursive step - pass new array to process:
    int sum(int[] nums) {
      if (nums.length == 0) {
        return 0;
      } else {
        return nums[0] + sum(Arrays.copyOfRange(nums, 1, nums.length));
      }
    }
    

    Both these ways will find sum of array elements. But it will come with different cost:

    • first approach will use O(n) additional memory, since on each recursive step it requires to allocate only constant O(1) amount of memory and overall there are n steps

    • second approach will use O(n^2) additional memory, since on each recursive step it requires to allocate O(n) additional memory (when copy of original array is created) and overall there are n steps

    In practice, you will find different variations, and honestly what method is best, actually, is determined by the task at hand.