Java 递归时如何处理堆栈溢出错误?

Java 递归时如何处理堆栈溢出错误?,java,arrays,recursion,stack-overflow,Java,Arrays,Recursion,Stack Overflow,我目前正在开发一个程序,可以读取大小为n*n的“地图”。此“地图”由字符和*组成,其中表示水,*表示土地。该计划的目的是计算地图上出现的“岛屿”数量,其中“岛屿”是指完全被水包围的任何一块陆地(*)。如果两块陆地(*)在传统的八个方向中的任何一个方向上相连,则它们被视为一个岛屿。为了便于参考,我将在最后放一个输入和输出示例 我的代码(我也将包括在内)解析一个与映射匹配的2Dchar数组,每当遇到*时递增int numOfIslands。然后,它将*替换为,并使用递归查找并替换该“孤岛”的其余部分

我目前正在开发一个程序,可以读取大小为n*n的“地图”。此“地图”由字符
*
组成,其中
表示水,
*
表示土地。该计划的目的是计算地图上出现的“岛屿”数量,其中“岛屿”是指完全被水包围的任何一块陆地(
*
)。如果两块陆地(
*
)在传统的八个方向中的任何一个方向上相连,则它们被视为一个岛屿。为了便于参考,我将在最后放一个输入和输出示例

我的代码(我也将包括在内)解析一个与映射匹配的2D
char
数组,每当遇到
*
时递增
int numOfIslands
。然后,它将
*
替换为
,并使用递归查找并替换该“孤岛”的其余部分,然后继续遍历。它目前适用于较小的输入大小,例如包含的示例输入。然而,问题是,它需要能够在
n=1000
的输入大小上运行,而我当前在这么大的输入大小上运行它时,会出现堆栈溢出错误。我应该如何处理StackOverflow错误?我想这与在递归过程中为了“卸载”堆栈而中断有关,但我真的不知道如何开始这样做,我的互联网研究也没有什么成果

我的遍历和递归代码,其中
map[][]
是与输入文件匹配的2D
char
数组:

//traverses the map, looking for 1s, and then sets that location to 0 before running testLocation on it
public static void traverse() {
    for (int col = 0; col < n; col++) {
        for (int row = 0; row < n; row++) {
            if (map[row][col] == '*') {
                map[row][col] = '.';
                testLocation(row, col);
                numOfIslands++;
            }
        }
    }
}    

//takes in a land location, and makes that land, along with the rest of the "island" 0s
public static void testLocation(int row, int col) {
    //test upper left
    if (row > 0 && col > 0) {
        if (map[row - 1][col - 1] == '*') {
            map[row - 1][col - 1] = '.';
            testLocation(row - 1, col - 1);
        }
    }

    //test upper
    if (row > 0) {
        if (map[row - 1][col] == '*') {
            map[row - 1][col] = '.';
            testLocation(row - 1, col);
        }
    }

    //test upper right
    if (row > 0 && col < n - 1) {
        if (map[row - 1][col + 1] == '*') {
            map[row - 1][col + 1] = '.';
            testLocation(row - 1, col + 1);
        }
    }

    //test left
    if (col > 0) {
        if (map[row][col - 1] == '*') {
            map[row][col - 1] = '.';
            testLocation(row, col - 1);
        }
    }

    //test right
    if (col < n - 1) {
        if (map[row][col + 1] == '*') {
            map[row][col + 1] = '.';
            testLocation(row, col + 1);
        }
    }

    //test lower left
    if (row < n - 1 && col > 0) {
        if (map[row + 1][col - 1] == '*') {
            map[row + 1][col - 1] = '.';
            testLocation(row + 1, col - 1);
        }
    }

    //test lower
    if (row < n - 1) {
        if (map[row + 1][col] == '*') {
            map[row + 1][col] = '.';
            testLocation(row + 1, col);
        }
    }

    //test lower right
    if (row < n - 1 && col < n - 1) {
        if (map[row + 1][col + 1] == '*') {
            map[row + 1][col + 1] = '.';
            testLocation(row + 1, col + 1);
        }
    }
}
样本输出:

The total number of islands is 3

如果不详细研究您的算法,我怀疑您多次检查同一单元格,导致搜索深度呈指数级增长。避免这种情况的一种简单但有效的方法是保留已测试单元的缓存,并在找到的地方使用缓存结果


然而,如果你真的需要那么深的一堆

在某些平台上,您可以使用
公共线程(ThreadGroup,Runnable target,String name,long stackSize)
获得一个可以访问更多堆栈的线程

然而Javadoc警告说:“在某些平台上,stackSize参数的值可能没有任何影响。”


如果算法确实需要堆栈,但JRE的调用堆栈太小,则可以将其转换为在堆内存中使用自己的堆栈的版本

例如,这里是使用递归的古老经典的河内塔:

void move(int num, int from, int to, int using) {
    if(num == 1) {
        println("%d to %d\n", from, to);
    } else {
        move(num-1, from, using, to);
        move(1, from, to, using);
        move(num-1, using, to, from);
    }
}
您可以执行以下等效操作:

Stack<Task> stack = new Stack<>();
stack.push(new Task(num, from, to, using));
while(!s.empty()) {
    doTask(stack);
}

void doTask(Stack<Task> stack) {
    Task t = stack.pop();

    if t.num == 1 {
        printf("%d to %d\n", from, to);
    } else {
        stack.push(new Task(t.num-1, t.using, t.to, t.from));
        stack.push(new Task(1, t.from, t.to, t.using));
        stack.push(new Task(t.num-1, t.from, t.using, t.to));
    }
}
Stack Stack=新堆栈();
push(新任务(num、from、to、using));
而(!s.empty()){
点任务(堆栈);
}
void点任务(堆栈){
Task t=stack.pop();
如果t.num==1{
printf(“%d到%d\n”,从,到);
}否则{
push(新任务(t.num-1,t.using,t.to,t.from));
stack.push(新任务(1,t.from,t.to,t.using));
push(新任务(t.num-1,t.from,t.using,t.to));
}
}

它不是递归的,因此不会创建深层调用堆栈。但它仍然使用了使用后进先出堆栈作为“待办事项列表”的相同原理,只是现在后进先出数据结构是堆的一部分。

如果不详细研究您的算法,我怀疑您多次检查同一单元格,导致搜索深度呈指数增长。避免这种情况的一种简单但有效的方法是保留已测试单元的缓存,并在找到的地方使用缓存结果


然而,如果你真的需要那么深的一堆

在某些平台上,您可以使用
公共线程(ThreadGroup,Runnable target,String name,long stackSize)
获得一个可以访问更多堆栈的线程

然而Javadoc警告说:“在某些平台上,stackSize参数的值可能没有任何影响。”


如果算法确实需要堆栈,但JRE的调用堆栈太小,则可以将其转换为在堆内存中使用自己的堆栈的版本

例如,这里是使用递归的古老经典的河内塔:

void move(int num, int from, int to, int using) {
    if(num == 1) {
        println("%d to %d\n", from, to);
    } else {
        move(num-1, from, using, to);
        move(1, from, to, using);
        move(num-1, using, to, from);
    }
}
您可以执行以下等效操作:

Stack<Task> stack = new Stack<>();
stack.push(new Task(num, from, to, using));
while(!s.empty()) {
    doTask(stack);
}

void doTask(Stack<Task> stack) {
    Task t = stack.pop();

    if t.num == 1 {
        printf("%d to %d\n", from, to);
    } else {
        stack.push(new Task(t.num-1, t.using, t.to, t.from));
        stack.push(new Task(1, t.from, t.to, t.using));
        stack.push(new Task(t.num-1, t.from, t.using, t.to));
    }
}
Stack Stack=新堆栈();
push(新任务(num、from、to、using));
而(!s.empty()){
点任务(堆栈);
}
void点任务(堆栈){
Task t=stack.pop();
如果t.num==1{
printf(“%d到%d\n”,从,到);
}否则{
push(新任务(t.num-1,t.using,t.to,t.from));
stack.push(新任务(1,t.from,t.to,t.using));
push(新任务(t.num-1,t.from,t.using,t.to));
}
}

它不是递归的,因此不会创建深层调用堆栈。但是它仍然使用与使用后进先出堆栈作为“待办事项列表”相同的原则,只是现在后进先出数据结构是堆的一部分。

如果您绝对想保持递归,除了增加堆栈大小之外,无需做太多。否则,考虑使用迭代算法,而不是递归算法。可以使用不同的算法。是常用的,并且只使用了两个过程。我不完全理解这个算法,但我认为这一个有一个隐式的基本情况,而不是显式的。在这里定义一个显式的基本情况是否有助于减少一些麻烦?如果您绝对希望保持递归,除了增加堆栈大小之外,无需做太多事情。否则,考虑使用迭代算法,而不是递归算法。可以使用不同的算法。是常用的,并且只使用了两个过程。我不完全理解这个算法,但我认为这一个有一个隐式的基本情况,而不是显式的。在这里定义一个明确的基本情况可能有助于减少一些麻烦吗?这是关于缓存的第一部分——我在一个类似的练习中遇到了类似的问题,检测一个单元格是否被困在围棋板上。如上所述,我使用缓存解决了这个问题。您可能会从我的解决方案中得到一些东西:(请参阅选中的