非递归实现二叉树中序遍历、前序遍历和后序遍历

递归方法下的二叉树的中序遍历比较简单,大概过程就是func(root->left)👉visit(root)👉func(root->right),如果不使用递归的方法的话,就要用栈来模拟这个过程。
将一个递归函数转换成为非递归的解决方法,最重要的就是搞明白在递归过程中函数栈的调用过程。我们可以思考一下内存状况:

每次函数执行,一路向左,只有当某棵左子树完全访问完才会访问根节点,之后在对根节点的右子树重复同样的过程,因此我们可以这样进行实现

1.当前结点是树根👉推入栈中👉去找它的左孩子
2.重复1,直到一个结点不是树根,也就是该节点为空,这意味着进入该结点的结点(父结点)的左子树完全访问完成,那么我们访问这个结点,完成中序过程。
3.我们访问完该结点的左子树和该结点,那么应该访问该结点的右子树,这也是同样的过程,只要走到它的右子树上即可,此时当前结点又成为了一个树根,重复1过程

代码如下:

Node* node = root;
stack<Node*> st;
while(node!=NULL||!st.empty()){
	if(node!=null){
		st.push(node);
		node=node->left;
	}
	else{
		node=st.top();
		st.pop();
		visit(node);
		node=node->right;
	}
}

类似的,我们可以写出前序的非递归:

Node* node = root;
stack<Node*> st;
while(node||!st.empty()){
	if(node){
		visit(node);
		st.push(node);
		node=node->left;
	}
	else{
		node=st.top();
		st.pop();
		node=node->right;
	}
}

后序遍历需要判断上次访问的节点是左子树还是右子树,如果该节点的左子树已被访问过则置标记为left;若右子树被访问过,则置标记为right:

//定义枚举类型:Tag
enum Tag{left,right};
//自定义新的类型,把二叉树节点和标记封装在一起
typedef struct
{
    BTNode* node;
    Tag tag;
}TagNode;    
//后序遍历  
void PostOrderWithoutRecursion2(BTNode* root)
{
    if (root == NULL)
        return;
    stack<TagNode> s;
    TagNode tagnode;
    BTNode* p = root;
    while (!s.empty() || p)
    {
        while (p)
        {
            tagnode.node = p;
            //该节点的左子树被访问过
            tagnode.tag = Tag::left;
            s.push(tagnode);
            p = p->lchild;
        }
        tagnode = s.top();
        s.pop();
        //左子树被访问过,则还需进入右子树
        if (tagnode.tag == Tag::left)
        {
            //置换标记
            tagnode.tag = Tag::right;
            //再次入栈
            s.push(tagnode);
            p = tagnode.node;
            //进入右子树
            p = p->rchild;
        }
        else//右子树已被访问过,则可访问当前节点
        {
            cout << setw(4) << (tagnode.node)->data;
            //置空,再次出栈(这一步是理解的难点)
            p = NULL;
        }
    }
    cout << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值