Как сделать в порядке обхода 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;
}
}
ключ-это родительские указатели (или возможность мутировать дерево), но вам нужно постоянное количество дополнительного состояния (например, счетчик программы следующей подпрограммы).
- установите v в корень.
- пока у v есть левый ребенок, установите v на его левый ребенок.
- выход В.
- Если V-корень, то вернитесь.
- установите p в родительское значение V.
- Если правый ребенок p-v, установите v в p и перейдите к шагу 4.
- выход p.
- Если у p есть правильный ребенок, установите v в правый ребенок p и перейдите к Шагу 2.
- установите 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;
}
}
}