简体   繁体   中英

How to avoid StackOverflowError caused by the number of recursion calls in Java?

I want to solve this problem: https://www.hackerrank.com/challenges/find-median ,ie find the median element in unsorted array. To do this I perform quickselect algorithm.

My program works correctly on my computer. However when I submitted in the system it gave me StackOverflowError. I think this is because of the depth of the recursion calls. I guess I make too many recursion calls than Java allows (the error is caused by a test case with 10 001 numbers). Can someone suggest me how to avoid this?

This is my code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class FindMedian {
    static int res;

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(
                new InputStreamReader(System.in));
        String line1 = br.readLine();
        int N = Integer.parseInt(line1);
        String[] line2 = br.readLine().split(" ");
        int[] arr = new int[N];
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(line2[i]);
        }
        selectKth(arr, 0, N - 1);
        System.out.println(res);
    }

    public static void selectKth(int[] arr, int start, int end) { 
        // it is written to select K-th element but actually
        // it selects the median element
        if (start >= end) {
            res = arr[start];
            return;
        }
        int pivot = arr[start];
        int n = arr.length - 1;
        int i = start + 1;
        int j = end;
        while (i <= j) {
            while (arr[i] <= pivot) {
                i++;
            }
            while (pivot < arr[j]) {
                j--;
            }
            if (i < j) {
                int tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
                i++;
                j--;
            }
        }
        int tmp = arr[j];

        // j is the index of the last element which is bigger or equal to pivot
        arr[j] = pivot;

        arr[start] = tmp;
        if (n / 2 <= j) {
            selectKth(arr, start, j);
        } else {
            selectKth(arr, i, end);
        }
    }
}

A basic concept of IT science is to replace recursion with iteration. By using iteration you'll never run into "recursion too deep" errors. Every problem can be solved either by recursion or by iteration.

See my link for details. http://www.refactoring.com/catalog/replaceRecursionWithIteration.html

Since you only call selectKth at the very end of itself (it's practically tail recursive already), it's trivial to unroll your recursion into an iteration.

func foo(param) {
   if (param == bar)  //bailout condition
     return;
   x = doStuff(param); //body of recursive method
   foo(x); //recursive call
}

Can be rewritten as

func foo(param) {
    x = param;
    while ( x != bar ) {
      x = doStuff( x );
    }
}

In your case the only tricky bit is the last if :

if(n/2 <= j){
    selectKth(arr, start, j);
}
else{
    selectKth(arr, i, end);
}

So your method will look like this:

public static void selectKth(int[] arr, int start, int end) { //it is written seclect K-th element but actually it selects the median element
    while( start < end ) {
        //method body as is up to the last `if`
        if(n/2 <= j) { //instead of recursion we just adjust start and end, then loop
             end = j;
        }
        else{
             start = i;
        }
    }
    res = arr[start];
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM