Хобрук: Ваш путь к мастерству в программировании

Использование рекурсии для поиска по бинарному дереву и проверки, содержит ли оно строку

Я пытаюсь использовать рекурсию для поиска по двоичному дереву и возврата true или false в зависимости от того, содержит ли двоичное дерево строку. Вот мой код

public class BinaryTree {
private String data;
private BinaryTree leftChild;
private BinaryTree rightChild;

public BinaryTree() {
    data = null;
    leftChild = null;
    rightChild = null;
}

public BinaryTree(String d) {
    data = d;
    leftChild = new BinaryTree();
    rightChild = new BinaryTree();
}

// This constructor is unchanged
public BinaryTree(String d, BinaryTree left, BinaryTree right) {
    data = d;
    leftChild = left;
    rightChild = right;
}

// Get methods
public String getData() {
    return data;
}

public BinaryTree getLeftChild() {
    return leftChild;
}

public BinaryTree getRightChild() {
    return rightChild;
}

// Set methods
public void setData(String d) {
    data = d;
}

public void setLeftChild(BinaryTree left) {
    leftChild = left;
}

public void setRightChild(BinaryTree right) {
    rightChild = right;
}

 public boolean contains(String d) {
    return d != null && (this.getData().equals(d) ||
            contains(this.getLeftChild().getData()) ||
            contains(this.getRightChild().getData()));
}

Итак, моя проблема связана с методом contains, поскольку он продолжает выдавать мне stackoverflow.error . Я надеялся, что смогу получить помощь в этом, спасибо заранее.


  • Во-первых, ваша рекурсия не повторяет правильные данные. Вместо contains(this.getLeftChild().getData()) вы должны вызывать this.getLeftChild().contains(d). Вы не передаете строку запроса своим дочерним объектам, вы передаете данные о своем дочернем элементе в запрос к вашему корневому объекту, поэтому ваш рекурсивный вызов не спрашивает, есть ли у моего дочернего объекта данные содержат эту строку?, вместо этого он спрашивает, содержат ли мои данные данные моего ребенка? 29.03.2018
  • что сказал @rdowell, но не забудьте проверить, что getLeftChild() не возвращает null, прежде чем пытаться вызвать его метод .contains. 29.03.2018

Ответы:


1

Вы можете попробовать это:

public boolean contains(String d)
{
  // Not contained if specified string is null
  if (d == null)
    return (false);

  // OK if specified string equals our data
  if ((data != null) && data.equals(d))
    return (true);

  // OK if contained in left tree
  if ((leftChild != null) && leftChild.contains(d))
    return (true);

  // OK if contained in right tree
  if ((rightChild != null) && rightChild.contains(d))
    return (true);

  // Otherwise, it's not OK
  return (false);

} // contains
29.03.2018
  • правильная идея, хотя лично я бы не стал использовать методы доступа в собственных методах класса - это накладные расходы, которые не служат никакой полезной цели и просто замедляют код. 29.03.2018
  • Я согласен. Обновил ответ. 29.03.2018

  • 2

    То, что вы делаете, это не рекурсия. Вы спрашиваете:

    boolean found = tree.contains(candidate)
    

    Правильно?

    Ваш код расширяет это до

    boolean found = candidate != null && (tree.getData.equals(d) || LEFT || RIGHT)
    

    где ЛЕВЫЙ

    contains(tree.getLeftChild().getData())
    

    который вообще не сравнивает строку-кандидат с левыми данными, а скорее расширяется до

    candidate != null && (tree.getData.equals(candidate) || LEFT || RIGHT)
    

    что приводит к бесконечному циклу, вызывающему StackOverflow.

    Вы должны переформулировать класс как

    public class Node {
        Node left, right;
        String data
        public boolean contains(String d);
    }
    

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

    29.03.2018

    3

    При каждом рекурсивном вызове this ссылается на один и тот же объект, и поэтому вы продолжаете передавать одни и те же значения снова и снова. Вам нужно передать BinaryTree ссылок в качестве параметра.

    private boolean contains(String data, BinaryTree node) {
        if (node == null) {
            return false;
        }
    
        return node.getData().equals(data) || contains(data, node.getLeftChild())
             || contains(data, node.getRightChild());
    
    }
    

    Основной (общедоступный) contains необходимо передать искомую строку и корень вышеуказанному методу.

    29.03.2018
  • это абсолютно не способ проверки узла, потому что метод contains уже имеет ссылку на запрашиваемый узел — это this. Вы предложили, как бы вы сделали это на языке, отличном от OO. 29.03.2018
  • @Alnitak Я не понимаю, что ты говоришь. Что, если вы перейдете в поддерево? Вам нужно получить доступ к значению узла (и ссылкам на его поддерево) с помощью геттеров 29.03.2018
  • что из этого? Поддерево имеет те же самые методы, что и корень дерева. Взгляните на ответ Роберта Кока. 29.03.2018
  • @Alnitak Я не вызываю метод contains на дочернем узле - я использую рекурсию. Вы имеете в виду доступ к нему напрямую, как node.leftChild? 29.03.2018
  • Я говорю о том, что вы передаете node в качестве явного параметра. Вам не нужно - метод contains имеет неявную переменную node, переданную в качестве переменной this. 29.03.2018
  • @Alnitak Да. Но как с этого момента вы будете вызывать метод contains рекурсивно для левого/правого узла, не передавая его? 29.03.2018
  • @Alnitak Решение Роберта Кока не является рекурсивным. Я сосредотачиваюсь на рекурсивном, поскольку ОП застрял на этом подходе. 29.03.2018
  • другое решение абсолютно рекурсивно. Вам не нужно передавать его, потому что вы можете вызвать leftChild.contains(d), после чего вы выполнили рекурсивный вызов, и передали дочерний узел в качестве неявный параметр this для этого вызова. 29.03.2018
  • @Alnitak Вызов leftChild.contains(d) не является рекурсивным. Вы должны вызвать метод contains для текущего объекта. Метод contains, который вы вызываете, находится на каком-то другом объекте leftChild в том, что вы говорите 29.03.2018
  • Это совершенно неправильное определение рекурсии. 29.03.2018
  • @Alnitak Можете ли вы указать источник, который утверждает, что вызывает метод для другого объекта (того же типа) как рекурсию 29.03.2018
  • Можете ли вы указать на тот, который показывает, что ваше определение верно? Семантически вызов метода другого объекта и передача этого объекта в качестве параметра идентичны рекурсии; текущий объект this фактически просто передается как скрытый параметр. Но в языке ООП передача этой переменной явно — плохой дизайн. 29.03.2018
  • @Alnitak Но я не вызываю метод для другого объекта. Я каждый раз вызываю один и тот же метод с разными ссылками. Итак, я все же не могу соотнести то, что вы говорите, с этим 29.03.2018
  • Нет, другой парень вызывает (тот же) метод для другого объекта, а вы утверждали, что это не рекурсия. Вы ошибаетесь, потому что этот другой объект является частью одной и той же (рекурсивно определенной) структуры. 29.03.2018
  • Новые материалы

    Расистский и сексистский робот, обученный в Интернете
    Его ИИ основан на предвзятых данных, которые создают предрассудки. Он словно переходит из одного эпизода в другой из серии Черное зеркало , а вместо этого представляет собой хронику..

    Управление состоянием в микрофронтендах
    Стратегии бесперебойного сотрудничества Микро-фронтенды — это быстро растущая тенденция в сфере фронтенда, гарантирующая, что удовольствие не ограничивается исключительно бэкэнд-системами..

    Декларативное и функциональное программирование в стиле LINQ с использованием JavaScript с использованием каррирования и генератора ...
    LINQ - одна из лучших функций C #, которая обеспечивает элегантный способ написания кода декларативного и функционального стиля, который легко читать и понимать. Благодаря таким функциям ES6,..

    Структуры данных в C ++ - Часть 1
    Реализация общих структур данных в C ++ C ++ - это расширение языка программирования C, которое поддерживает создание классов, поэтому оно известно как C с классами . Он используется для..

    Как я опубликовал свое первое приложение в App Store в 13 лет
    Как все началось Все началось три года назад летом после моего четвертого класса в начальной школе. Для меня, четвертого класса, лето кажется бесконечным, пока оно не закончится, и мой отец..

    Что в лицо
    Очерк о возвращении физиогномики и о том, почему мы должны это приветствовать. История начинается со странной науки. Р. Тора Бьорнсдоттир, Николас О. Рул. Видимость социального класса по..

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