Обхождане на n-арно дърво без използване на рекурсия

Как мога да обходя n-арно дърво без да използвам рекурсия?

Рекурсивен начин:

traverse(Node node)
{
    if(node == null)
        return;

    for(Node child : node.getChilds()) {
        traverse(child);
    }
}

person ako    schedule 13.05.2011    source източник
comment
Всеки рекурсивен алгоритъм може да бъде намален до цикъл + стек, така че това не е голямо ограничение. Направете го с някой от алгоритмите, които могат да бъдат намерени в Google.   -  person Matthew Scharley    schedule 13.05.2011
comment
В добро настроение съм, затова ви преведох вашия алгоритъм. Като цяло обаче не е много трудно да се направи и бързо гугъл щеше да открие много резултати за това. Обхождането на дървото е много добре дефиниран проблем от този момент в историята.   -  person Matthew Scharley    schedule 13.05.2011
comment
Просто интересно @ako. Защо искате да го направите без рекурсия? Има ли причина или това е просто въпрос за печелене на репутация?   -  person Mihran Hovsepyan    schedule 13.05.2011
comment
@mihran защо потребител ще се присъедини към стека, за да спечели репутация на сайт, който не е използвал преди?   -  person Nicolas78    schedule 14.05.2011


Отговори (3)


Можете да направите това без рекурсия и без стек. Но трябва да добавите два допълнителни указателя към възела:

  1. Родителският възел. Така че можете да се върнете при родителя, ако сте приключили.
  2. Текущият дъщерен възел, за да знаете кой да вземете следващия.

    • For each node, you handle all the kids.
    • Ако едно дете е обработено, вие проверявате дали има следващо дете и го обработвате (актуализиране на текущото).
    • Ако всички деца са обработени, върнете се при родителя.
    • Ако възелът е NULL, излезте.

С псевдокод това изглежда така:

traverse(Node node) {
  while (node) {
    if (node->current <= MAX_CHILD) {
      Node prev = node;
      if (node->child[node->current]) {
        node = node->child[node->current];
      }
      prev->current++;
    } else {
      // Do your thing with the node.
      node->current = 0; // Reset counter for next traversal.
      node = node->parent;
    }
  }
}
person Toon Krijthe    schedule 13.05.2011
comment
Добре, сега разбирам. Това решение обаче налага страшно много ограничения върху структурата на данните. И това дори не говори за многопоточност. - person Björn Pollex; 13.05.2011
comment
@Space_C0wb0y, вярно, това е просто още един начин да се покаже, че рекурсивният начин е най-ясният. - person Toon Krijthe; 14.05.2011

Това, което правите, е по същество DFS на дървото. Можете да премахнете рекурсията, като използвате стек:

traverse(Node node) {
    if (node==NULL)
        return;

    stack<Node> stk;
    stk.push(node);

    while (!stk.empty()) {
        Node top = stk.pop();
        for (Node child in top.getChildren()) {
            stk.push(child);
        }
        process(top);
    }
}

Ако искате BFS, използвайте опашка:

traverse(Node node) {
    if (node==NULL)
        return;

    queue<Node> que;
    que.addRear(node);

    while (!que.empty()) {
        Node front = que.deleteFront();
        for (Node child in front.getChildren()) {
            que.addRear(child);
        }
        process(front);
    }
}

В случай, че искате някакъв друг начин за преминаване, ще трябва да следвате същия подход, макар и с различна структура от данни, за да съхраните възела. Може би приоритетна опашка (ако искате да оцените функция във всеки възел и след това да обработвате възли въз основа на тази стойност).

person BiGYaN    schedule 15.05.2011
comment
BFS = Търсене първо в ширина, DFS = Търсене първо в дълбочина - person Marduk; 30.06.2021

Не е даден език, така че в псевдо-псевдокод:

traverse(Node node)
{
  List<Node> nodes = [node];

  while (nodes.notEmpty) {
    Node n = nodes.shift();

    for (Node child in n.getChildren()) {
      nodes.add(child);
    }

    // do stuff with n, maybe
  }
}

Имайте предвид, че това е обхождане първо в ширина, за разлика от обхождането първо в дълбочина, дадено във въпроса. Трябва да можете да направите обхождане първо в дълбочина, като popвключите последния елемент от nodes списъка, вместо shiftвключвайки първия.

person Matthew Scharley    schedule 13.05.2011
comment
ако използвате pop вместо shift, това няма да е DFS, тъй като пак ще започне с основния възел. DFS започва с най-дълбокия (ляв) лист. - person Marco M.; 26.09.2012
comment
@MarcoM. Решението на OP също първо ще търси корена, тъй като използва рекурсия на опашката. Мисля, че е достатъчно близо за повечето практически приложения. - person Matthew Scharley; 27.09.2012