二叉树遍历的递归与非递归研究

昨个笔试让写二叉树中序的非递归,写的不好,回去仔细研究了一下,发现还是没对二叉树的遍历深刻的理解。

非递归的遍历其实也是按着遍历的定义来的,只要好好理解,就会很容易写出来,其实写出来不难,难在如何把它写好。

非递归需要用棧来存储节点。

struct btree {
    char data;
    struct btree * left;
    struct btree * right;
};

//先序递归
void PreOrder(struct btree* b){
    if(b != NULL){
        cout << b -> data;
        PreOrder(b -> left);
        PreOrder(b -> right);
    }
}
//非递归
void PreOrder_(struct btree * b){
    stack<struct btree *> sta;

    while(b || !sta.empty()){
        while(b){            //一直访问左子树,并输出
            cout << b -> data;
            sta.push(b);
            b = b -> left;
        }
        b = sta.top();       //左子树为空,就把棧顶弹出,并去访问棧顶元素的右子树
        sta.pop();
        b = b -> right;
    }
}
非递归抽象下,其实也就是访问根节点,然后访问左子树,并入棧,左子树空就去访问右子树。

//中序
void Inorder(struct btree *b){
    if(b != NULL){
        Inorder(b -> left);
        cout << b -> data;
        Inorder(b -> right);
    }
}
//非递归
void Inorder_(struct btree *b){
    stack<struct btree*> sta;
    while(b || !sta.empty()){
        while(b){                //访问左节点并入棧
            sta.push(b);
            b = b -> left;
        }
        b = sta.top();           //左子树为空则访问根节点,之后在访问右子树
        cout << b -> data;
        sta.pop();               //这里每次pop之后去访问右子树,这样就保证一个节点只入棧一次
        b = b -> right;
    }
}
后序的非递归我想了很久,我一直再考虑是否可以不借助变量记录访问过的节点,来遍历,但是发现不可行,因为访问顺序就是左右中,左右没访问之前,中是不能出棧的。之后就是考虑这个变量如何设置,最开始想到的是用set来记录访问过的节点,这个方法肯定是要被pass的,要是1w节点存储空间太大,后来看到有人用数组做的,比set好很多了,可是代码实现写的不够好,后来想了一种简单的判别方式:

类似中序的非递归,后序是左右中,也就是说每次从棧顶取出元素的时候,说明这个节点的左子树肯定遍历完了,之后只需要去判断它的右子树,重点就是判断右子树是否被入棧过,如果没有入棧过,就如中序一样访问右子树,如果右子树访问过了,那么这个右节点一定是上一次被输出的(或者说最近一次被输出)节点,因为访问顺序是左右中,访问完右子树就该根节点了,所以只需要一个指针记录最近被输出的节点,与当前要访问节点的右节点做对比,相同就直接输出当前节点,不同就去访问右子树。

说的有点罗嗦,看代码结合理解。

//后序
void PostOrder(struct btree *b){
    if(b != NULL){
        PostOrder(b -> left);
        PostOrder(b -> right);
        cout << b -> data;
    }
}
//非递归
void PostOrder_(struct btree* b){
    stack<struct btree *> sta;
    struct btree * p = NULL;

    while(b || !sta.empty()){
        
        while(b){
            sta.push(b);
            b = b -> left;
        }

        b = sta.top();
        if(b -> right == NULL || b -> right == p){
            cout << b -> data;
            p = b;            //记录被访问的节点
            sta.pop(); 
            b = NULL;         //置为NULL,避免上边的while循环
        }else{
            b = b -> right;
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值