Как сделать в порядке обхода BST без рекурсии или стека, но с использованием родительских указателей?

можно ли выполнить итеративный обход по порядку на BST, узел которого имеет Родительский указатель (родитель корня null) без использования visited флаг или stack?

я погуглил и не нашел ответа. Дело в том, как я могу знать - на определенном узле - что я только что пришел к нему, и я закончил все под ним?

8 ответов


вы можете сделать это, вам просто нужно запомнить последний посещенный узел вместе с текущим узлом. Это не запрещено постановкой задачи: both visited флаг на каждом узле и stack есть (в худшем случае) O(n), помня, что последний узел просто O(1).

В C#, алгоритм может выглядеть так:

static void Walk(Node node)
{
    Node lastNode = null;
    while (node != null)
    {
        if (lastNode == node.Parent)
        {
            if (node.Left != null)
            {
                lastNode = node;
                node = node.Left;
                continue;
            }
            else
                lastNode = null;
        }
        if (lastNode == node.Left)
        {
            Output(node);

            if (node.Right != null)
            {
                lastNode = node;
                node = node.Right;
                continue;
            }
            else
                lastNode = null;
        }
        if (lastNode == node.Right)
        {
            lastNode = node;
            node = node.Parent;
        }
    }
}

вот еще один способ сделать это. Я думаю, что это по существу эквивалентно ответу Свика, но избегает дополнительной переменной. Эта версия реализована в Python:

node=root
if node is not None:
  while node.left is not None:
    node=node.left
  while node is not None:
    output(node)
    if node.right is not None:
      node=node.right
      while node.left is not None:
        node=node.left
    else:
      while node.parent is not None and node.parent.right is node:
        node=node.parent
      node=node.parent

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


С помощью svick'правильная идея (см. Его ответ), это проверен код на C++. Обратите внимание, что я не тестировал его код и даже не смотрел на него, я просто взял его идею и реализовал свою собственную функцию.

void in_order_traversal_iterative_with_parent(node* root) {
node* current = root;
node* previous = NULL;

while (current) {
    if (previous == current->parent) { // Traversing down the tree.
        previous = current;
        if (current->left) {
            current = current->left;
        } else {
            cout << ' ' << current->data;
            if (current->right)
                current = current->right;
            else
                current = current->parent;
        }
    } else if (previous == current->left) { // Traversing up the tree from the left.
        previous = current;
        cout << ' ' << current->data;
        if (current->right)
            current = current->right;
        else
            current = current->parent;
    } else if (previous == current->right) { // Traversing up the tree from the right.
        previous = current;
        current = current->parent;
    }
}

cout << endl;
}

public void inorderNoStack() {
    if (root == null) {
        return;
    }

    // use the previous to always track the last visited node
    // helps in deciding if we are going down/up
    Node prev = null;

    Node curr = root;

    while (curr != null) {
        // going down
        if (prev == null || prev.left == curr || prev.right == curr) {
            if (curr.left != null) {
                prev = curr;
                curr = curr.left;
                continue;
            } else {

                visitn(curr);

                if (curr.right != null) {
                    prev = curr;
                    curr = curr.right;
                    continue;
                } else {
                    // swap states
                    prev = curr;
                    curr = prev.parent;
                }
            }
        }

        // going up after left traversal
        if (curr != null && prev == curr.left) {

            visitn(curr);

            if (curr.right != null) {
                prev = curr;
                curr = curr.right;
                continue;
            } else {
                // swap states
                prev = curr;
                curr = prev.parent;
            }
        }

        // going up after right traversal
        if (curr != null && prev == curr.right) {
            // swap states
            prev = curr;
            curr = prev.parent;
        }
    }
}

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

https://github.com/skanagavelu/Algorithams/blob/master/src/tree/InOrderTraversalIterative.java


Шаг 1: напишите функцию, которая возвращает преемника в порядке

Шаг 2: начиная с самого левого узла, найдите преемника в порядке, пока нет

    public class TreeNode {
      int data;
      TreeNode left;
      TreeNode right;
      TreeNode parent;
    }

    public class TreeUtility {
      public void inorderNoRecursion(TreeNode root) {
        TreeNode current = leftmostNode(root);
        while(current != null) {
          System.out.println(current.data);
          current = inorderSuccessor(current);
        }
      }

      public TreeNode inorderSuccessor(TreeNode node) {
        if (node.right!=null) {
          return leftmostNode(node.right);
        }

        TreeNode p = node.parent;
        TreeNode c = node;
        while(p!=null && c != p.left) {
          c = p;
          p = p.parent;
        }
        return p;
      }

      private TreeNode leftmostNode(TreeNode node) {
        while (node.left != null) {
          node = node.left;
        }
        return node;
      }
    }

ключ-это родительские указатели (или возможность мутировать дерево), но вам нужно постоянное количество дополнительного состояния (например, счетчик программы следующей подпрограммы).

  1. установите v в корень.
  2. пока у v есть левый ребенок, установите v на его левый ребенок.
  3. выход В.
  4. Если V-корень, то вернитесь.
  5. установите p в родительское значение V.
  6. Если правый ребенок p-v, установите v в p и перейдите к шагу 4.
  7. выход p.
  8. Если у p есть правильный ребенок, установите v в правый ребенок p и перейдите к Шагу 2.
  9. установите v в p и перейдите к шагу 4.

это в C++:

void InOrder(Node *r)
{
   if(r==NULL)
         return;

   Node *t=r;

   while(t!=NULL)
       t=t->left;

  while(t!=r)
  {
     if(t==(t->parent->left))
     {
        cout<<t->parent->data;
        t=t->parent->right;
       if(t!=NULL)
      {
       while(t!=NULL)
          t=t->left;
      } 
      if(t==NULL)
          t=t->parent;
     }
     if(t==t->parent->right)
     {
        t=t->parent;
     }
  }
}