数据结构学习笔记-二叉树非递归遍历及层序遍历

157 阅读3分钟

非递归遍历

非递归遍历要用到栈

前序

根结点-左孩子-右孩子

//前序遍历的非递归算法
void preorder(bitree *t){
    bitree *temp = t;//定义一个树节点,用它来遍历
    while(temp != NULL || s.top != 0) {
        //先遍历左孩子,并输出。
        while(temp != NULL) {
            printf("%4d",temp->data);
            push(temp);
            temp = temp->lchild;
        }
        //当左孩子遍历完后,取栈顶,找右孩子。此时循环还没有结束,再遍历它的左孩子,直至孩子全部遍历结束。
        if(s.top != 0) {
            temp = pop();
            temp = temp->rchild;
        }
    }
    printf("\n");
}

中序

左孩子-根结点-右孩子

//中序遍历的非递归算法
void midOrder(bitree *t) {
    bitree *temp = t;
    while(temp != NULL||s.top != 0) {
        //先把左孩子入栈,所有左孩子入栈结束
        while(temp != NULL) {
            push(temp);
            temp = temp->lchild;
        }
        //左孩子入栈结束,取栈顶,输出栈顶元素,遍历右孩子
        if(s.top != 0) {
            temp = pop();
            printf("%4d",temp->data);
            temp = temp->rchild;
        }
    }
    printf("\n");
}

后序

左孩子-右孩子-根节点

具体算法:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是否是第一次出现在栈顶。

//后序遍历的非递归算法
void laorder(bitree *root) {
    bitree *temp = root;
    while(temp!=NULL||s.top!=0) {
        while(temp!= NULL) {
            // 当前节点首次被访问
            temp->cishu=1;       
            push(temp);
            temp=temp->lchild;
        }
        if(s.top!=0) {
            temp=pop( );
            // 第一次出现在栈顶
            if(temp->cishu == 1) {
                temp->cishu++;
                push(temp);
                temp=temp->rchild;
            } else {
                //第二次输出并制空,防止陷入死循环
                if(temp->cishu==2) {
                    printf("%4d",temp->data);
                    temp=NULL;
                }
            }
        }
    }
    printf("\n");
}

层序遍历

层序遍历要用到队列,队列具有先进先出特性。

实现逻辑:层序遍历的思路是,创建一个队列,先将根节点(A)入队,然后用front指针将根节点记下来,再将根节点出队,接下来看front节点(也就是刚才的根节点)有没有左孩子或右孩子,如果有,先左(B)后右(C)入队,最后输出front节点的值,只要队列还不为空,就说明还没有遍历完,就进行下一次循环,这时的队头元素(front)则为刚才入队的左孩子(B),然后front出队,再把它的左右孩子拉进来(如果有),因为队列的先进先出性质,B的左右孩子DE是排在C后面的,然后输出B,下一次循环将会拉人C的左右孩子FG,最后因为FG没有左右孩子,一直出队,没有入队元素,队列迟早会变为空,当队列为空时,整颗树就层序遍历完成了,结束循环。

void LevelOrder(TreeNode *T) {
    Queue<TreeNode *> q;
    TreeNode *front;

    if (T == NULL)return;

    q.push(T);
    while (!q.empty()) {
        front = q.front();
        q.pop();

        if (front->left) {
            q.push(front->left);
        }

        if (front->right) {
            q.push(front->right);
        } 
        printf("%c ", front->data);
    }
}

参考 二叉树的非递归遍历(前序中序后序非递归C语言)