14

My problem is that I usually get a java.lang.StackOverflowError when I use recursion. My question is - why does recursion cause stackoverflow so much more than loops do, and is there any good way of using recursion to avoid stack overflow?

This is an attempt to solve problem 107, it works well for their example but runs out of stack space for the problem it self.

//-1 16 12 21 -1 -1 -1 16 -1 -1 17 20 -1 -1 12 -1 -1 28 -1 31 -1 21 17 28 -1 18 19 23 -1 20 -1 18 -1 -1 11 -1 -1 31 19 -1 -1 27 -1 -1 -1 23 11 27 -1
public class tries
{
    public static int n=7,min=Integer.MAX_VALUE;
    public static boolean[][] wasHere=new boolean[n][60000];
    public static void main(String[] args)
    {
        int[] lines=new int[n]; Arrays.fill(lines, -1000); lines[0]=0;
        int[][] networkMatrix=new int[n][n];
        Scanner reader=new Scanner(System.in);
        int sum=0;
        for(int k=0; k<n; k++)
        {
            for(int r=0; r<n; r++)
            {
                networkMatrix[k][r]=reader.nextInt();
                if(networkMatrix[k][r]!=-1) sum+=networkMatrix[k][r];
                Arrays.fill(wasHere[k], false);
            }
        }
        recursive(lines,networkMatrix,0,0);
        System.out.println((sum/2)-min);
    }
    public static void recursive(int[] lines, int[][] networkMatrix, int row,int lastRow)
    {       
        wasHere[row][value((int)use.sumArr(lines))]=true;
        if(min<sum(lines)) return;
        if(isAllNotMinus1000(lines)) min=sum(lines); 
        int[][] copyOfMatrix=new int[n][n];
        int[] copyOfLines;
        for(int i=0; i<n; i++)
        {
            copyOfLines=Arrays.copyOf(lines, lines.length);
            for(int k=0; k<n; k++)  copyOfMatrix[k]=Arrays.copyOf(networkMatrix[k], networkMatrix[k].length);
            if(i!=0&&copyOfMatrix[i][row]!=0) copyOfLines[i]=copyOfMatrix[i][row];
            copyOfMatrix[i][row]=0; copyOfMatrix[row][i]=0;
            if(networkMatrix[row][i]==-1) continue;
            if(wasHere[i][value((int)use.sumArr(copyOfLines))]) continue;
            if(min<sum(copyOfLines)) continue;
            recursive(copyOfLines,copyOfMatrix,i,row);
        }
    }
    public static boolean isAllNotMinus1000(int[] lines)
    {
        for(int i=0; i<lines.length; i++) {if(lines[i]==-1000) return false;}
        return true;
    }
    public static int value(int n)
    {
        if(n<0) return (60000+n);
        return n;
    }
    public static int sum(int[] arr)
    {
        int sum=0;
        for(int i=0; i<arr.length; i++) 
        {
            if(arr[i]==-1000) continue;
            sum+=arr[i];
        }
        return sum;
    }
}
user2705335
  • 357
  • 1
  • 3
  • 8
  • Are you sure that your recursions are not calling themselves infinitely? It might help to include your code. – David Starkey Aug 21 '13 at 21:59
  • code is very complicated, and because it works for small numbers I am certain this isn't the case. Adding the code is a good idea regardless, though. I just hope it wouldn't drift too much off topic. – user2705335 Aug 21 '13 at 22:02
  • When you have Integer.MAX_VALUE lines it has only done the first recursion of 7 in each for loop some level n deep, but it has (7^(n+1)-1/6)-n-1 iterations to go, most if them hitting base case. The started for loops will add 6 times n times Integer.MAX_VALUE lines though. – Sylwester Aug 21 '13 at 23:13

10 Answers10

24

why does recursion cause stackoverflow so much more than loops do

Because each recursive call uses some space on the stack. If your recursion is too deep, then it will result in StackOverflow, depending upon the maximum allowed depth in the stack.

When using recursion, you should be very careful and make sure that you provide a base case. A base case in recursion is the condition based on which the recursion ends, and the stack starts to unwind. This is the major reason of recursion causing StackOverflow error. If it doesn't find any base case, it will go into an infinite recursion, which will certainly result in error, as Stack is finite only.

Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • Recurive procedures can cause StackOverFlow even when base case is defined. Think about quick sort with bad split – mightyWOZ Jun 13 '21 at 07:48
9

In most cases, a stack overflow occurs because a recursive method was ill-defined, with a non-existent or unreachable ending condition, which causes the stack memory space to be exhausted. A correctly written recursion should not produce a stack overflow.

However, there are situations where a method can produce a stack overflow even if it was correctly implemented. For instance:

  • A fast-growing (say, exponential) recursion. For example: the naive recursive implementation of the Fibonacci function
  • A very big input data, that will eventually cause the stack space to be exhausted

Bottom line: it all depends on the particular case, it's impossible to generalize regarding what causes a stack overflow.

Óscar López
  • 232,561
  • 37
  • 312
  • 386
3

Each recursive call uses some space on the stack (to house anything specific to that one call, such as arguments, local variables, etc.). Thus, if you make too many recursive calls (either by not correctly providing a base case or just by trying to do too many recursive calls), then there is not enough room to provide space for it all, and you end up with a StackOverflow.

The reason why loops do not have this problem is that each iteration of a loop does not use its own unique space (i.e. if I loop n times, I don't need extra space to do the n+1st loop).

Dennis Meng
  • 5,109
  • 14
  • 33
  • 36
1

The reason why the recursion causes stack overflow is because we fail to establish when the recursion should stop, and thus the function/method will keep calling itself "forever" (until it causes the error). You will have the same problem even if you are using loops, if you have something as the following:

bool flag = true;
while (flag == true){
   count++;
}

Since flag will always be true, the while loop will never stop until it gives you the stack overflow error.

Anna
  • 538
  • 2
  • 4
  • 13
  • 2
    This is wrong. What you have here is an infinite loop that will cause the count to overflow (assuming it's an integer/long) but there is no chance that this will cause an StackOverflowError. Indeed, infinite loops are widely used. – u6f6o Jul 02 '17 at 15:10
1

Every level of recursion that you go down, you are add state information to the runtime stack. This information is stored in an activation record and contains information like which variables are in scope and what their values are. Loops do not have extra activation records each time you loop so they take less memory.

In certain situations your recursion may go deep enough that it causes the stack to overflow but there are ways to help prevent this from happening. When working with recursion, I usually follow this format:

public obj MyMethod(string params) {
    if (base-case) {
        do something...
    } else {
        do something else...
        obj result = MyMethod(parameters here);
        do something else if needed..
    }
}

Recursion can be super effective and do things that loops cannot. Sometimes you just get to a point where recursion is the obvious decision. What makes you a good programmer is being able to use it when it is not completely obvoius.

Roman Vottner
  • 12,213
  • 5
  • 46
  • 63
TJS
  • 80
  • 2
  • 10
0

When properly used, recursion will not produce a StackOverflowError. If it does, then your base case is not being triggered, and the method keeps calling itself ad infinitum. Every method call that does not complete remains on the stack, and eventually it overflows.

But loops don't involve method calls by themselves, so nothing builds up on the stack and a StackOverflowError does not result.

rgettman
  • 176,041
  • 30
  • 275
  • 357
  • 2
    You are wrong. Some heavy recursive methods requires to do big job, it is not sign of not proper usage. –  Jul 27 '18 at 18:45
0

Every time you call a method, you consume a "frame" from the stack, this frame is not released until the method returns, it doesn't happen the same with loops.

morgano
  • 17,210
  • 10
  • 45
  • 56
0

recursion causes stack overflow cause all the previous calls are in memory. so your method calls itself with new parameters, then that again calls itself. so all these calls stack up and normally can run out of memory. loops store the results normally in some variables and call the methods which is like a new fresh call to methods, after each call, the caller methods ends and returns results.

Ashish Thukral
  • 1,445
  • 1
  • 16
  • 26
0

As in my opinion, getting error as StackOverFlow in Recursion due to :

  1. not implemented the recursion correctly which results in infinite recursion, so check out the base case, etc.

  2. If your input is large, it preferred to use Tail Recursion to avoid StackOverflow.

Aarrav Agarwal
  • 81
  • 1
  • 1
  • 9
-1

Here for loop is used inside the recursive function. When the recursive function is called, for(int i=0; i<n; i++) the value of i is initialized to zero, as it calls itself, the value of i will again be initialized to zero and it conitues infintely. This will lead you to Stack overflow error.

Solution: Avoid for loop inside recursive function; instead go for while or do-while and initialize the value of i outside recursive function

marla
  • 43
  • 5
  • A `while` loop in a recursive function (or anywhere) could also cause infinite looping – varontron Jan 22 '20 at 07:28
  • @varontron this infinite looping is caused by the initialization. If you do initialization outside the recursive function, you are good to use while loop – marla Jan 22 '20 at 12:30