二叉树的遍历(递归、非递归)

二叉树遍历的基本概念

1、二叉树遍历原理:二叉树的遍历是指从根结点出发,按照某种次序依次访问所有结点,使得每个结点被访问一次且仅被访问一次。
2、二叉树遍历方法:
(1)前序遍历:若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。
(2)中序遍历:若树为空,则空操作返回,否则从根结点开始,中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
(3)后序遍历:若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点。
(4)层序遍历:若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。

二叉树遍历举例:

这里写图片描述
二叉树的前序遍历序列: ABCDEFGH
二叉树的中序遍历序列:CBEDFAGH
二叉树的后续遍历序列:CEFDBHGA
二叉树的层序遍历举例:ABGCDHEF

二叉树遍历算法

1、前序遍历算法
(1)递归算法

void PreOrder(BtNode *ptr)
{
    if (NULL != ptr)
    {
        printf("%c ",ptr->data);
        PreOrder(ptr->leftChild);
        PreOrder(ptr->rightChild);
    }
}

(2非递归算法
思路1:
对于任一结点pCur:
1)访问结点pCur,并将结点pCur入栈,结点pCur一直左走 ;
2)判断结点pCur的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点pCur,循环至 1);若不为空,则将P的左孩子置为当前的结点pCur;
3)直到pCur为NULL并且栈为空,则遍历结束。

void NicePreOrder(BtNode *ptr)
{
    if (ptr == NULL)
    {
        return;
    }
    stack<BtNode *> stackTreeNodes; 
    BtNode *pCur = ptr;//指向当前要访问的结点  
    while(pCur != NULL || !stackTreeNodes.empty()) //判断pCur是否为NULL是为处理右单子树出现的情况
    {  
        while(pCur != NULL)//一直往左走  
        {  
            printf("%c ",pCur->data);  
            stackTreeNodes.push(pCur);  
            pCur = pCur->leftChild;  
        }

        if(!stackTreeNodes.empty())//在出栈和访问栈顶元素之前要先检查是否为空  
        {  
            pCur = stackTreeNodes.top();//pCur刚跳出上面的while循环时值为NULL 故要将其指向当前的栈顶结点  
            stackTreeNodes.pop();  
            pCur = pCur->rightChild;  
        }  
    }  
}

思路2:
1)先判断树是否为空,若为空则返回空操作,否则将根结点入栈;
2)然后判断栈为不为空,不为空则访问栈顶元素并弹出栈顶元素;
3)判断右子树是否为空,不为空则将右子树根结点压入栈,再判断左子树是否为空,不为空则将左子树根结点压入栈,继续进行2)操作,直到栈为空为止。

void NicePreOrder(BtNode *ptr)
{
    if(NULL == ptr) 
    {
        return ;
    }
    Stack st;
    Init_Stack(&st);
    push(&st,ptr);
    while(!empty(&st))
    {
        ptr = top(&st); 
        pop(&st);
        printf("%c ",ptr->data);
        if(ptr->rightchild != NULL)
            push(&st,ptr->rightchild);
        if(ptr->leftchild != NULL)
            push(&st,ptr->leftchild);
    }
}

2、中序遍历算法
(1)递归算法

void InOrder(BtNode *ptr)
{
    if (ptr != NULL)
    {
        InOrder(ptr->leftChild);
        printf("%c ",ptr->data);
        InOrder(ptr->rightChild);
    }
}

(2)非递归算法
思路:
对于任一结点pCur:
1)将结点pCur入栈,结点pCur一直左走
2)判断结点pCur的左孩子是否为空,若为空,访问结点pCur,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点pCur,循环至 1);若不为空,则将P的左孩子置为当前的结点pCur;
3)直到pCur为NULL并且栈为空,则遍历结束。

void NiceInOrder(BtNode *ptr)
{
    if (ptr == NULL)
    {
        return;
    }
    stack<BtNode *> stackTreeNodes;
    BtNode *pCur = ptr;//指向当前子树的根结点  
    while(pCur != NULL || !stackTreeNodes.empty())  
    {  
        while(pCur != NULL)//一直往左走  
        {  
            stackTreeNodes.push(pCur);  
            pCur = pCur->leftChild;  
        }

        if(!stackTreeNodes.empty())//在出栈和访问栈顶元素之前要先检查是否为空  
        {  
            pCur = stackTreeNodes.top();//pCur刚跳出上面的while循环时值为NULL 故要将其pucr当前的栈顶结点  
            printf("%c ",pCur->data);
            stackTreeNodes.pop();  
            pCur = pCur->rightChild;  
        }  
    }  
}

3、后序遍历算法
(1)递归算法

void PastOrder(BtNode *ptr)
{
    if (ptr != NULL)
    {
        PostOrder(ptr->leftChild);
        PostOrder(ptr->rightChild);
        printf("%c ",ptr->data);
    }
}

(2)非递归算法
思路1:
1)设立两个指针pCur (初始指向根结点),和preVisted(指向被访问结点),对于任一结点pCur,将其入栈;
2)然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,此时要判断此结点是否满足输出条件,即:当前结点的右孩子如果为空或者已经被访问,则访问当前结点 ,否则将pCur置为其右结点,直到pCur为NULL并且栈为空,则遍历结束。

void NicePastOrder(BtNode *ptr)
{
    if(ptr == NULL)
    {  
        return;  
    }  
    stack<BtNode *> stackTreeNodes;  
    BtNode *pCur = ptr;//指向当前子树的根结点  
    BtNode *preVisted = NULL;//指向前一被访问的结点  
    while(pCur != NULL || !stackTreeNodes.empty())  
    {  
        while(pCur != NULL)//一直向左走  
        {  
            stackTreeNodes.push(pCur);  
            pCur = pCur->leftChild;  
        }
        if(!stackTreeNodes.empty())//在出栈和访问栈顶元素之前要先检查是否为空  
        {  
            //pCur刚跳出上面的while循环时值为NULL,故要将pCur指向当前的栈顶结点  
            pCur = stackTreeNodes.top();
            if(pCur->rightChild == NULL || pCur->rightChild==preVisted)//当前节点的右孩子如果为空或者已经被访问,则访问当前节点  
            {  
                printf("%c ",pCur->data);  
                stackTreeNodes.pop();  
                preVisted = pCur;//设置当前结点已被访问过
                pCur = NULL;//防止当前结点又被压入栈中 
            }
            else//如果当前结点的右子结点存在且没被访问过  
            {  
                pCur = pCur->rightChild;  
            }  
        }  
    }  
}

思路2:本质上和思路1一样。
1)设立标记结点指针,以此来判断右子树是否已被访问过,先将根结点入栈;
2)然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,将该结点出栈,并判断该结点的右孩子是否为空或者已经被访问(即等于标记结点指针),若是则访问该结点 ,并将该结点指针赋值给标记结点,该结点指针置为空,否则将该结点重新压入栈顶,直到当前结点指针和栈都为空,则遍历结束。

void NicePastOrder(BtNode *ptr)
{
    if(ptr == NULL) return ;
    Stack st;
    Init_Stack(&st);
    BtNode *tag = NULL;
    while(ptr != NULL || !empty(&st))
    {
        while(ptr != NULL)
        {
            push(&st,ptr);
            ptr = ptr->leftchild;
        }
        ptr = top(&st); 
        pop(&st);
        if(ptr->rightchild == NULL || ptr->rightchild == tag)
        {
            printf("%c ",ptr->data);
            tag = ptr;
            ptr = NULL;
        }
        else
        {
            push(&st,ptr);
            ptr = ptr->rightchild;
        }
    }
}

4、层序遍历算法
方法1:有点类似非递归的前序遍历,只不过换成了队列。
思路:
1)先判断树是否为空,不为空则将根结点入队列;
2)然后判断队列是否为空,不为空则出队头元素,并访问此结点;
3)先判断当前结点左子树是否为空,不为空则将左孩子结点入队列,再判断当前结点的右子树是否为空,不为空则将右孩子结点入队列,继续进行2)操作,直到队列为空为止。

void LevelOrder(BtNode *ptr)
{
    assert(ptr != NULL);
    queue<BtNode *> queueTreeNodes;  
    queueTreeNodes.push(ptr);

    while(!queueTreeNodes.empty())  
    {  
        BtNode *pNode=queueTreeNodes.front();  
        queueTreeNodes.pop();
        printf("%c ",pNode->data);

        if(pNode->leftChild!=NULL)
        {  
            queueTreeNodes.push(pNode->leftChild);
        }  

        if(pNode->rightChild!=NULL)
        {  
            queueTreeNodes.push(pNode->rightChild);  
        }  
    }  
}

方法2:
思路:使用vector保存每个结点的指针,存入vector中的元素顺序是按照层数递增并且每层按照从左到右。由于vector的底层是数组,所以可以通过下标依次访问每个结点的数据。

void LevelOrder(BTNode *ptr)
{
    if (ptr == NULL)
    {
        return;
    }
    vector<BTNode*> vec;
    BTNode *p = ptr;
    vec.push_back(p);

    //存放当前结点在vector中的下标
    int cur = 0;
    while (cur < vec.size())
    {
        printf("%c", vec[cur]->data);
        if (vec[cur]->leftChild != NULL)
        {
            vec.push_back(vec[cur]->leftChild);
        }

        if (vec[cur]->rightChild != NULL)
        {
            vec.push_back(vec[cur]->rightChild);
        }
        ++cur;
    }
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值