如何在查找SCC时克服此堆栈溢出问题?

时间:2017-05-01 22:02:14

标签: java version-control stack-overflow dfs kosaraju-algorithm

这是我用来编写SCC使用Kosaraju的双通算法的代码。当我运行main方法时,我在SCC.revDFS上得到一个StackOverFlowError。如何进行大量递归调用时,如何避免堆栈溢出错误?

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.Scanner;

public class SCC {
    int n = 875714;
    Map<Integer,List<Integer>> adjList;
    Map<Integer,List<Integer>> adjListRev;
    int[] ft;
    int t;
    int s;
    boolean[] marked;
    int[] leaders;

    public SCC() {
        init();
        t = 0;
        s = 0;
        marked = new boolean[n + 1];
        leaders = new int[n + 1];
    }

    void init() {
        adjList = new HashMap<Integer,List<Integer>>();
        adjListRev = new HashMap<Integer,List<Integer>>();
        ft = new int[n + 1];
        List<Integer> adj;
        try {
            Scanner scanner = new Scanner (new InputStreamReader(this.getClass().
                    getClassLoader().getResourceAsStream("SCC.txt")));
            while(scanner.hasNextLine()) {
                String s = scanner.nextLine().trim();
                String[] num = s.split(" ");
                if (!adjList.containsKey(Integer.parseInt(num[0]))) {
                    adjList.put(Integer.parseInt(num[0]), new ArrayList<Integer>());
                }
                adj = adjList.get(Integer.parseInt(num[0]));
                adj.add(Integer.parseInt(num[1]));
                adjList.put(Integer.parseInt(num[0]), adj);

                if (!adjListRev.containsKey(Integer.parseInt(num[1]))) {
                    adjListRev.put(Integer.parseInt(num[1]), new ArrayList<Integer>());
                }
                adj = adjListRev.get(Integer.parseInt(num[1]));
                adj.add(Integer.parseInt(num[0]));
                adjListRev.put(Integer.parseInt(num[1]), adj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void DFS_Loop() {

        for (int i = 1; i < n + 1; i++) {
            marked[i] = false;
        }
        for (int i = n; i > 0; i--) {
            if (!marked[i]) {
                revDFS(i);
            }
        }
        for (int i = 1; i < n + 1; i++) {
            marked[i] = false;
            leaders[i] = 0;
        }
        for (int i = n; i > 0; i--) {
            if (!marked[ft[i]]) {
                s = ft[i];
                DFS(ft[i]);
            }
        }
    }

    public void revDFS(int i) {
        marked[i] = true;
        List<Integer> edges = adjListRev.get(i);
        if (edges != null) {
            for (int j: edges) {
                if (!marked[j]) {
                    revDFS(j);
                }
            }
        }
        t += 1;
        ft[t] = i;
    }

    public void DFS(int i) {
        marked[i] = true;
        leaders[s] += 1;
        List<Integer> edges = adjList.get(i);
        if (edges != null) {
            for (int j: edges) {
                if (!marked[j]) {
                    DFS(j);
                }
            }
        }
    }

    public static void main(String[] args) {
        SCC scc = new SCC();
        scc.DFS_Loop();
        Arrays.sort(scc.leaders);
        for (int i = scc.n; i < scc.n - 5; i--) {
            System.out.println(scc.leaders[i]);
        }
    }
}

3 个答案:

答案 0 :(得分:0)

也许您可以尝试将逻辑转换为迭代方法。另外,请检查是否正确处理了基本和边缘情况。

答案 1 :(得分:0)

将递归函数转换为迭代函数的基本思想是递归函数使用堆栈中的参数。

因此,您可以创建一个堆栈并将值推入其中,然后在循环中使用它们。

public void _revDFS(int _i) {
    LinkedList<Integer> stack = new LinkedList<>();
    stack.push(_i);

    while(!stack.isEmpty()){
        int i = stack.pop();
        marked[i] = true;
        List<Integer> edges = adjListRev.get(i);
        if (edges != null) {
            for (int j: edges) {
                if (!marked[j]) {
                    stack.push(j);
                    //revDFS(j);
                }
            }
        }
        t += 1;
        ft[t] = i;
    }
}

我无法真正测试它,看看我是否犯了某种错误,revDFS是一个有很多副作用的函数而且它没有返回值,所以有点困难用它来推理。

但要点是,不是调用函数本身,而是将边缘索引推送到堆栈然后使用它们。

将以相反的顺序处理子边缘,因此如果要保持原始处理的顺序相同,则应以相反的顺序读取边缘:

            ListIterator<Integer> li = edges.listIterator(edges.size());
            while(li.hasPrevious()){
                int j = li.previous();
                if (!marked[j]) {
                    stack.push(j);
                    //revDFS(j);
                }
            }

答案 2 :(得分:0)

你已经递归地实现了你的Dfs函数,导致&#34;堆栈溢出&#34;。要解决此问题,您需要使用堆栈数据结构来实现它。 请参阅下面的链接了解更多动机 https://github.com/sinamalakouti/MyFavoriteAlgorithmProblems