Ошибка компиляции при реализации поиска в глубину рекурсивно

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

Поскольку я не слишком хорошо разбираюсь в рекурсии, я не понимаю, почему я получаю следующую ошибку.

This method must return a result of type LinkedList.Node

Код, который у меня есть в настоящее время:

public Node DFSTime(Node node){
    if (node == null){
        System.out.println("No path found");
        return null;
    }
    else if (node.element.equals(destinationAirport)){
        return node;
    }
    else{
        stack.push(node);
        DFSTime(node.nextNode);            
    }
}

Это незаконченный код, так как мне все еще нужно реализовать некоторую логику, однако я не понимаю, как устранить ошибку. Есть ли что-то очень простое, чего мне не хватает?

В вашем методе есть путь, по которому он ничего не вернет. А именно в else-случае. Поймите, что значит попасть в дело else, а затем спросите себя, какое Node вы хотите вернуть в этом случае. Затем верните его, и Java будет счастлива.

Zabuzard 11.05.2022 21:59

Обратите внимание, что вы не должны использовать null в качестве контрольного значения, чтобы указать на отсутствие результата. Вместо этого вы должны использовать класс Optional. Приводит к пустому опциону, если он не найден, и к опциону, заполненному узлом, если он найден.

Zabuzard 11.05.2022 22:00

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

Zabuzard 11.05.2022 22:02

Я думал об использовании стека для отслеживания пути, по которому я шел по графу. И поскольку это атрибут класса, в котором я реализовал DFS. Так что мне не нужно использовать рекурсию, поскольку я ничего не буду делать с результатами рекурсивного метода?

Muhammad Zaid 11.05.2022 22:05

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

Zabuzard 11.05.2022 22:07
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
5
39
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

У вас должен быть оператор return по умолчанию в конце функции после закрытия else.

Просто нормальный return null должен работать, верно?

Muhammad Zaid 11.05.2022 21:55

Не обязательно. Зависит от логики, которую вы хотите создать.

Zabuzard 11.05.2022 21:59

@Забузард Видим?

Muhammad Zaid 11.05.2022 22:00

Подробности читайте в других моих комментариях.

Zabuzard 11.05.2022 22:01

Да, и вы, вероятно, захотите что-то сделать с «DFSTime (node.nextNode)», например. вернуть DFSTime (узел. следующий узел);

candino 11.05.2022 22:01

В условных блоках методов (if-else) вам нужно убедиться, что вы возвращаете соответствующий тип из всех условных операторов, чтобы не было ошибки времени компиляции. В вашем случае блок else рекурсивно вызывает DFSTime(..), ничего не возвращая.

Возможно, вы захотите вернуть ссылку, которая вызывается через рекурсивный вызов, как показано ниже:

public Node DFSTime(Node node){
        if (node == null){
            System.out.println("No path found");
            return null;
        }
        else if (node.element.equals(destinationAirport)){
            return node;
        }
        else{
            stack.push(node);
            Node node = DFSTime(node.nextNode);
            return node;

        }
    }
Ответ принят как подходящий

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

Тем важнее то, что ваш текущий подход — не верно.

it seems to easier to have a recursive method instead of a looping one since I am trying to do Depth First Traversal for a Graph

Есть важные вещи, которые следует учитывать:

  • Поле nextNode очень подозрительно. Если каждый Node содержит ссылку, указывающую только на один узел, фактически созданная вами структура данных по определению является не График, а Односвязный список. И нет смысла реализовывать DFS для списка. Каждый узел должен указывать на коллекция узлов, а не на один узел.

  • Вы должны как-то различать непосещенные узлы и узлы, которые являются уже посетил. Или же вы могли бы и с бесконечной рекурсией. Для этого вы можете определить поле booleanisVisited внутри Node или поместить каждый посещаемый узел в HashSet.

  • Поскольку вы решили создать рекурсивную реализацию DFS, вам не необходимо создать файл куча. Это требуется только для итеративной реализации.

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

  • Используйте геттеры и сеттеры вместо прямого доступа к полям. Это предпочтительная практика в Java.

Ваш метод может выглядеть так (не обязательно, чтобы element был типа String, это просто иллюстрация общей идеи):

public Node DFSTime(Node node, String destinationAirport){
    if (node == null || node.isVisited()) {
        return null;
    }
    if (node.getElement().equals(destinationAirport)) {
        System.out.println("The destination airport was found");
        return node;
    }
    
    node.setVisited(true);
    for (Node neighbour: node.getNeighbours()) {
        Node result = DFSTime(neighbour, destinationAirport);
        if (result != null) return result;
    }
    return null;
}

И узел может выглядеть так:

public class Node {
    private String element;
    private List<Node> neighbours;
    private boolean isVisited;
    
    public Node(String element, List<Node> neighbours) {
        this.element = element;
        this.neighbours = neighbours;
    }
    
    public void setVisited(boolean visited) {
        isVisited = visited;
    }
    
    public boolean isVisited() {
        return isVisited;
    }
    
    public void addNeighbours(Node neighbour) {
        neighbours.add(neighbour);
    }
    
    public String getElement() {
        return element;
    }
    
    public List<Node> getNeighbours() {
        return neighbours;
    }
}

Другие вопросы по теме