递归时如何处理StackOverflow错误?

时间:2017-05-05 14:54:33

标签: java arrays recursion stack-overflow

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

我的代码(我也将包含)解析与地图匹配的2D char数组,每当遇到int numOfIslands时递增*。然后它用*替换.,并使用递归来查找和替换该“岛”的其余部分,然后继续进行遍历。它目前适用于较小的输入尺寸,例如包括的样本输入。但是,问题是它需要能够在n = 1000的输入大小上运行,并且当我尝试在大的输入大小上运行它时,我当前得到StackOverflow错误。我该怎么做才能处理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

1 个答案:

答案 0 :(得分:2)

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

但是,如果你真的需要那么深的堆栈......

在某些平台上,您可以使用public Thread(ThreadGroup group, 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));
    }
}

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