Ошибка глубины поиска в Java, избежание StackOverflow с пустыми вычислениями? - PullRequest
0 голосов
/ 11 октября 2019

Работая над проектом класса, я заметил очень странную ошибку в моей системе.

Приведенный ниже код читает словарь доступных слов, и алгоритм DFS пытается найти маршрут от одного слова к другому слову, учитывая график слов (график строится, если есть грань отна b, если a отличается только одной буквой от b).

При выполнении моего кода по умолчанию (с RUN_DO_NOTHING_BLOCK = false) я всегда переполняюсь стеком на каждом устройстве. При запуске кода с RUN_DO_NOTHING_BLOCK = true это как-то решает проблему переполнения стека, и код работает отлично. Проблема в том, что RUN_DO_NOTHING_BLOCK буквально ничего не делает в коде.

Ввод, который нам небезразличен, - "смерть опухоли \ n" (эти два слова не связаны и заставляют DFSдля поиска во всей связанной области «смерти». Примером подключенного ввода является «death chops \ n».

Вот словарь, в котором читается код: dictionary.txt

Я знаю, что ошибки переполнения стека сильно зависят от размера, выделенного для стека вызовов и кучи, и отличаются от компьютера к компьютеру, но я заметил эту ошибку на каждом устройстве, на котором я запускал этот код, и я думаю, что вы сможетеСоздайте его заново. Причина, по которой я написал это, заключается в том, что я хочу знать, почему бесполезный код решил эту проблему.

import java.util.*;
import java.io.*;

public class Main {
    public static Set<String> dictionary;
    /**
     * The graph created from the dictionary.
     * Maps from a node to a set of edges connected to the node.
     */
    public static Map<String,Set<String>> graph;

    public static final boolean RUN_DO_NOTHING_BLOCK = false;

    public static void main(String[] args) {

        //initalization
        dictionary = makeDictionary();
        graph = createGraph(dictionary);

        Scanner scan = new Scanner(System.in);

        String startWord = scan.next(), endWord = scan.next();
        ArrayList<String> wordLadder = getWordLadderDFS(startWord, endWord);
        System.out.println(wordLadder);
    }

    public static ArrayList<String> getWordLadderDFS(String start, String end) {
        start = start.toUpperCase();
        end = end.toUpperCase();
        //the actual ladder
        ArrayList<String> ladder = new ArrayList<>();
        //all unvisited nodes
        Set<String> unVisitedNodes = new HashSet<>();
        unVisitedNodes.addAll(dictionary);

        //start the recursive search
        boolean found = getWordLadderDFSRecurse(start, end, unVisitedNodes, ladder);

        return ladder;
    }

    private static boolean getWordLadderDFSRecurse(String node, String end, Set<String> unVisitedNodes, ArrayList<String> ladder) {
        //if already visited this node then dont do anything
        if(node==null || !unVisitedNodes.contains(node)){
            return false;
        }
        ladder.add(node);

        if(node.equals(end)){
            return true;
        }

        unVisitedNodes.remove(node);

        //*************
        //THIS IS THE DO NOTHING BLOCK...
        if(RUN_DO_NOTHING_BLOCK) {
            for (String i : unVisitedNodes) {
                if (unVisitedNodes.contains(node)) {
                    unVisitedNodes.remove(node);
                }
            }
        }
        //**************

        //recurse deeper
        Set<String> neighbors = graph.get(node);

        //go through all neighbors and try to recurse into them
        //go through neighbors in order though, from most promising to least
        for (String neighbor : neighbors) {

            //go in
            boolean found = getWordLadderDFSRecurse(neighbor, end, unVisitedNodes, ladder);
            //if we found a path then return
            if (found) {
                return true;
            }

        }

        //not found
        ladder.remove(node);
        return false;
    }

    /**
     * Creates the graph from the given dictionary of words.
     * @param dictionary
     * @return the graph in the form of an adjacency list.
     */
    private static Map<String, Set<String>> createGraph(Set<String> dictionary){

        Map<String, Set<String>> graph = new HashMap<>();

        for(String word: dictionary) {
            graph.put(word, new HashSet<>());
        }

        /**
         * Building from all possible one letter deviance of a given word is faster than looping through a huge dictionary again
         */
        for(String word1: dictionary){
            StringBuilder stringBuilder = new StringBuilder(word1);
            for(int i=0;i<word1.length();i++){
                for(char c='A'; c<='Z';c++) {
                    if(c==word1.charAt(i)){
                        continue;
                    }
                    stringBuilder.setCharAt(i, c);
                    if(dictionary.contains(stringBuilder.toString())){
                        graph.get(word1).add(stringBuilder.toString());
                    }
                }
                stringBuilder.setCharAt(i,word1.charAt(i));
            }
        }
        return graph;
    }


    public static Set<String> makeDictionary () {
        Set<String> words = new HashSet<String>();
        try {
            Scanner infile = new Scanner(new File("dictionary.txt"));
            while (infile.hasNext()) {
                words.add(infile.next().toUpperCase());
            }
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        return words;
    }
}

...