简体   繁体   English

如何避免Java中递归调用次数引起的StackOverflowError?

[英]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. 我想解决这个问题: https : //www.hackerrank.com/challenges/find-median ,即在未排序的数组中找到中位数元素。 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. 但是,当我在系统中提交时,它给了我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). 我猜我进行了太多的递归调用,而不是Java所允许的(错误是由具有10001数字的测试用例引起的)。 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. IT科学的基本概念是用迭代代替递归。 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 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. 由于您仅在其最末端调用selectKth (实际上已经是尾部递归了),因此将递归展开为迭代很简单。

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

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];
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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