Algorithm 基于堆栈的Euler树遍历问题

Algorithm 基于堆栈的Euler树遍历问题,algorithm,data-structures,tree,binary-tree,tree-traversal,Algorithm,Data Structures,Tree,Binary Tree,Tree Traversal,我想要一个使用Euler遍历()遍历二叉树的函数。当然,这是很容易实现的递归-我知道如何工作。但是现在我想用堆栈而不是递归来实现这个算法的迭代版本。我的想法是在堆栈上存储我们正在穿越的方向。我的代码不起作用,不知何故,我无法集中精力解决这个问题。你能给我一些关于如何解决这个问题的提示吗?以下是我目前的代码: #define LEFT (struct Node*) 0xBADF00D #define RIGHT (struct Node*) 0xDEADBEEF struct Node {

我想要一个使用Euler遍历()遍历二叉树的函数。当然,这是很容易实现的递归-我知道如何工作。但是现在我想用堆栈而不是递归来实现这个算法的迭代版本。我的想法是在堆栈上存储我们正在穿越的方向。我的代码不起作用,不知何故,我无法集中精力解决这个问题。你能给我一些关于如何解决这个问题的提示吗?以下是我目前的代码:

#define LEFT (struct Node*) 0xBADF00D
#define RIGHT (struct Node*) 0xDEADBEEF

struct Node { 
    int data; 
    struct Node* parent; 
    struct Node* left; 
    struct Node* right; 
}; 

void eulerTree(struct Node* root) 
{ 
    stack<struct Node*> s;

    s.push(root);
    s.push(RIGHT);
    s.push(root);
    s.push(LEFT);

    while(!s.empty()) {
        struct Node* direction = s.top(); s.pop();
        struct Node* node = s.top(); s.pop();

        visit(node);

        if(direction == LEFT) {
            if(node->left) {
                s.push(node->left);
                s.push(RIGHT);

                s.push(node->left);
                s.push(LEFT);
            }
        } 

        if(direction == RIGHT) {
            if(node->right) {
                s.push(node->right);
                s.push(RIGHT);

                s.push(node->right);
                s.push(LEFT);
            }
        }
    }
}
#定义左(结构节点*)0xBADF00D
#定义右(结构节点*)0xDEADBEEF
结构节点{
int数据;
结构节点*父节点;
结构节点*左;
结构节点*右;
}; 
void eulerTree(结构节点*根)
{ 
堆栈s;
s、 推(根);
s、 推(右);
s、 推(根);
s、 推(左);
而(!s.empty()){
结构节点*方向=s.top();s.pop();
结构节点*Node=s.top();s.pop();
访问(节点);
如果(方向==左){
如果(节点->左){
s、 推(节点->左);
s、 推(右);
s、 推(节点->左);
s、 推(左);
}
} 
如果(方向==右){
如果(节点->右侧){
s、 推送(节点->右侧);
s、 推(右);
s、 推送(节点->右侧);
s、 推(左);
}
}
}
}

首先考虑一个简单的二叉树:

           1
        2     3
这方面的Euler遍历是:
1231

您可以在这里看到模式:
root,root->left,root,root->right,root

因此,堆栈顺序应为:

root
root->left
root
root->right
root
但是如果你的根是一片叶子呢?然后不要推任何东西,只需打印值即可

此外,在左侧推送节点后,请确保将其设置为根节点的
0
,这样就不会一直推送它们

话虽如此,cpp中的代码将是:

编辑:

我之前发布的代码有一个bug。正确的代码如下:

void eulerTree(struct Node* root) 
{ 
    stack<struct Node*> s;

    s.push(root);


    while(!s.empty()) {

        struct Node* node = s.pop();

        visit(node);

        if(node->right) {
          s.push(node);
          s.push(node->right);
        }

        if(node->left) {
          s.push(node);
          s.push(node->left);
        }
        node->left = 0;
        node->right = 0;
    }
}

递归实现可能如下所示:

void euler(Node *n) {
    visit(n);
    if (n->left) {
        euler(n->left);
        visit(n);
    }
    if (n->right) {
        euler(n->right);
        visit(n);
    }
}
int state=0; // 0 => initial visit, 1 => just did left, 2 => just did right
Node *n = root;
while (n) {
    visit(n);

    if (n->left && state<1) {
        stack.push(n);
        n=n->left;
        state=0;
        continue;
    }

    if (n->right && state<2) {
        stack.push(n);
        n=n->right;
        state=0;
        continue;
    }

    if (stack.empty())
        break; // done

    Node *child=n;
    n = stack.pop();
    state = (child == n->left ? 1 : 2);
}
现在,无论何时进行递归调用,调用堆栈都被用来记住我们在代码中的位置以及我们在做什么。然后我们再次从顶部开始,当我们完成时,信息从堆栈中弹出,我们继续我们离开的地方

如果要使用自己的堆栈迭代地执行此操作,则必须自己执行相同的操作。你必须记住足够多的东西,才能继续你刚才说的话

我们必须记住我们在处理哪个节点,当然,还有两个递归调用,所以我们可能需要返回两个可能的位置。当我们从递归调用返回时,则:

  • 我们刚刚完成了
    n->left
    调用,应该继续检查
    n->right
    ;或
  • 我们刚刚完成了
    n->right
    呼叫,应该继续最后一次访问
    n
  • 我们可以在堆栈上存储一些额外的信息来区分这两种情况,但这对于这个特定的算法来说并不是必需的。从上面的描述中,您可以看到,我们可以根据返回的节点来区分这些情况-
    n->left
    n->right

    因此,只要将等待节点存储在堆栈中,我们就可以编写如下迭代版本:

    void euler(Node *n) {
        visit(n);
        if (n->left) {
            euler(n->left);
            visit(n);
        }
        if (n->right) {
            euler(n->right);
            visit(n);
        }
    }
    
    int state=0; // 0 => initial visit, 1 => just did left, 2 => just did right
    Node *n = root;
    while (n) {
        visit(n);
    
        if (n->left && state<1) {
            stack.push(n);
            n=n->left;
            state=0;
            continue;
        }
    
        if (n->right && state<2) {
            stack.push(n);
            n=n->right;
            state=0;
            continue;
        }
    
        if (stack.empty())
            break; // done
    
        Node *child=n;
        n = stack.pop();
        state = (child == n->left ? 1 : 2);
    }
    
    int state=0;//0=>初次就诊,1=>刚离开,2=>刚离开
    节点*n=根;
    while(n){
    访问(n);
    如果(n->left&&stateleft;
    状态=0;
    持续
    }
    如果(n->right&&stateright;
    状态=0;
    持续
    }
    if(stack.empty())
    中断;//完成
    节点*child=n;
    n=stack.pop();
    状态=(子==n->左?1:2);
    }
    
    将递归函数转换为迭代函数的最简单方法是在循环中使用堆栈来模拟递归调用。事实上,这正是递归调用同一函数时发生的情况,只是现在显式使用堆栈而不是使用调用堆栈进行计算。建议:不要使用不安全的pointer强制转换,声明两个Node类型的未使用的全局变量tree@MattTimmermans我同意。我在不破坏树的情况下添加了遍历代码。