Подсчет узлов в дереве в Java - PullRequest
20 голосов
/ 13 февраля 2009

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

Реализуйте метод count (), который возвращает количество узлов в дереве. Если у узла нет ни левого, ни правого дочернего элемента, соответствующий метод getXXChild() вернет null

class Tree {

  Tree getRightChild() {
    // Assume this is already implemented
  }

  Tree getLeftChild() {
    // Assume this is already implemented
  }

  int count() {
    // Implement me
  }
}

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

Ура, Tony

Ответы [ 15 ]

32 голосов
/ 13 февраля 2009
int count() {
  Tree right = getRightChild();
  Tree left = getLeftChild();
  int c = 1;                                      // count yourself!
  if ( right != null ) c += right.count();        // count sub trees
  if ( left != null ) c += left.count();          // ..
  return c;
}
19 голосов
/ 13 февраля 2009

Тривиальное рекурсивное решение:

int count() {
   Tree l = getLeftTree();
   Tree r = getRightTree();
   return 1 + (l != null ? l.count() : 0) + (r != null ? r.count() : 0);
}

Менее тривиальный нерекурсивный:

int count() {
    Stack<Tree> s = new Stack<Tree>();
    s.push(this);
    int cnt = 0;
    while (!s.empty()) {
        Tree t = s.pop();
        cnt++;
        Tree ch = getLeftTree();
        if (ch != null) s.push(ch); 
        ch = getRightTree();
        if (ch != null) s.push(ch); 
    }
    return cnt;
}

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

Редактировать: Вот вариант итеративного решения, который использует стек менее интенсивно:

int count() {
    Tree t = this;
    Stack<Tree> s = new Stack<Tree>();
    int cnt = 0;
    do {
        cnt++;
        Tree l = t.getLeftTree();
        Tree r = t.getRightTree();
        if (l != null) {
            t = l;
            if (r != null) s.push(r);
        } else if (r != null) {
            t = r;
        } else {
            t = s.empty() ? null : s.pop();
        }
    } while (t != null);
    return cnt;
}

Требуется ли вам более эффективное или более элегантное решение, естественно, зависит от размера ваших деревьев и от того, как часто вы собираетесь использовать эту процедуру. Вспомните, что сказал Хоар: «преждевременная оптимизация - корень всего зла».

11 голосов
/ 14 февраля 2009

Мне нравится это лучше, потому что оно гласит:

счетчик возврата для левого + счетчик для правой + 1

  int count() {
      return  countFor( getLeftChild() ) + countFor( getRightChild() ) + 1;
  }
  private int countFor( Tree tree )  { 
       return tree == null ? 0 : tree.count();
  }

Еще немного к грамотному программированию.

Кстати, мне не нравится соглашение о методах получения / установки, которое так часто используется в Java, я думаю, что вместо этого лучше использовать leftChild () :

  return countFor( leftChild() ) + countFor( rightChild() ) + 1;

Точно так же, как Хошуа Блох объясняет здесь http://www.youtube.com/watch?v=aAb7hSCtvGw в мин. 32: 03

Если вы понимаете, что код читается ...

НО, я должен признать, что соглашение get / set теперь является почти частью языка. :)

Для многих других частей следование этой стратегии создает самодокументирующийся код, что является чем-то хорошим.

Тони: Интересно, каков был ваш ответ в интервью?

4 голосов
/ 14 февраля 2009
class Tree {

  Tree getRightChild() {
    // Assume this is already implemented
  }

  Tree getLeftChild() {
    // Assume this is already implemented
  }

  int count() {
   return 1 
      + getRightChild() == null? 0 : getRightChild().count()
      + getLeftChild() == null? 0 : getLeftChild().count();
  }
}
4 голосов
/ 13 февраля 2009
return (getRightChild() == null ? 0 : getRightChild.count()) + (getLeftChild() == null ? 0 : getLeftChild.count()) + 1;

Или что-то в этом роде.

4 голосов
/ 13 февраля 2009

Примерно так должно работать:

int count()
{
    int left = getLeftChild() == null ? 0 : getLeftChild().count();
    int right = getRightChild() == null ? 0 : getRightCHild().count();

    return left + right + 1;
}
2 голосов
/ 09 декабря 2011

Реализация метода:

public static int countOneChild(Node root)
{
    ...
}

, который считает количество внутренних узлов в двоичном дереве, имеющем одного потомка. Добавьте функцию в программу tree.java.

2 голосов
/ 13 февраля 2009

Вы можете сосчитать дерево, пройдя его многими способами . Для простого предварительного обхода код будет (на основе определенных вами функций):

int count() {
    count = 1;
    if (this.getLeftChild() != null)
        count += this.getLeftChild().count();
    if (this.getRightChild() != null)
        count += this.getRightChild().count();
    return count;
}
1 голос
/ 02 июня 2017

Я сделал это по предварительному заказу рекурсии. Хотя он не совсем соответствует формату интервью с использованием localRoot, но я думаю, вы поняли идею.

private int countNodes(Node<E> localRoot, int count) {
    if (localRoot == null) 
        return count;     
    count++; // Visit root
    count = countNodes(localRoot.left, count); // Preorder-traverse (left)
    count = countNodes(localRoot.right, count); // Preorder-traverse (right)
    return count;
}

public int countNodes() {
   return countNodes(root, 0);
}
0 голосов
/ 06 апреля 2016
class Tree {

  Tree getRightChild() {
    // Assume this is already implemented
  }

Tree getLeftChild() {
    // Assume this is already implemented
 }

 int count() {
    if(this.getLeftChild() !=null && this.getRightChild()!=null) 
        return 1 + this.getLeftChild().count() + this.getRightChild().count();
    elseif(this.getLeftChild() !=null && this.getRightChild()==null)
        return 1 + this.getLeftChild().count();
    elseif(this.getLeftChild() ==null && this.getRightChild()!=null)
        return 1 + this.getRightChild().count();
    else return 1;//left & right sub trees are null ==> count the root node
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...