树的遍历(非递归)

描述

树的非递归遍历主要是利用栈来模拟递归的实现,跟递归一样,空间复杂度为O(h=lg(n)) (),时间复杂度为O(n)(每个节点都被压入栈一次,弹出栈一次,访问一次)

前序遍历

前序是先访问,再入栈

void PreorderTraverse(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;
    Stk.push(T);
    while(!Stk.empty())
    {
        pTree p = Stk.top();
        Stk.pop();
        cout << p->data << " ";
        if(p->Right != NULL)
            Stk.push(p->Right);
        if(p->Left != NULL)
            Stk.push(p->Left);
    }

}
/*
前序是先访问,再入栈;

1、如果T非空,访问栈顶元素,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,T指向右儿子。

*/

void PreorderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;


    while(T || !Stk.empty())
    {
        //如果T非空,访问栈顶元素,把T入栈,T指向左儿子。
        if(T)
        {
            cout << T->data << " ";
            Stk.push(T);
            T = T->Left;
        }
        //如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,T指向右儿子。
        else
        {
            T = Stk.top();
            Stk.pop();
            T = T->Right;
        }

    }


}

中序遍历

中序遍历则是先入栈,弹栈后再访问。

/*
中序则是先入栈,弹栈后再访问。

1、如果T非空,则把T入栈,T指向左儿子。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,进行访问,并把T指向其右儿子。
*/

void InorderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;


    while(T || !Stk.empty())
    {
        if(T)
        {
            Stk.push(T);
            T = T->Left;
        }

        else
        {
            T = Stk.top();
            Stk.pop();
            cout << T->data << " ";
            T = T->Right;
        }

    }
}

后序遍历

第一种采用两个栈来实现,跟前序遍历的第一种类似,不过就是先压入当前节点的左子树,后压入当前节点的右子树。得到的顺序是根节点-右子树-左子树,刚好是后序遍历的逆序,所以再利用一个栈解决就可以倒过来。

void PostorderTraverse(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk,Stk2;
    Stk.push(T);

    while(!Stk.empty())
    {
        pTree p = Stk.top();
        Stk.pop();
        Stk2.push(p);
        if(p->Left != NULL)
            Stk.push(p->Left);
        if(p->Right != NULL)
            Stk.push(p->Right);
    }

    while(!Stk2.empty())
    {
        cout << Stk2.top()->data << " ";
        Stk2.pop();
    }
}

第二站采用标记的方法,一直访问左子树并压入栈,直至为空,判断栈顶节点的右子树是否存在或是否已访问过,如果已访问过或不存在,则访问该节点元素,否则指向右子树

void PostorderTraverse3(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;
    pTree Curr = T;
    pTree Previsited = NULL;

    while(Curr != NULL || !Stk.empty())
    {
        //一直访问左子树,直至为空
        while(Curr != NULL)
        {
            Stk.push(Curr);
            Curr = Curr->Left;
        }
        Curr = Stk.top();
        //当前节点的右子树不存在或已经访问过,则访问该节点
        if(Curr->Right == NULL || Curr->Right == Previsited)
        {
            cout << Curr->data << " ";
            Stk.pop();
            Previsited = Curr;
            Curr = NULL;
        }
        else
        {
            Curr = Curr->Right;
        }
    }
}

层序遍历

层序遍历就是从上到下从左到右访问树的节点。采用FIFO(先进先出)的数据结构-队列

void LeverTraverse(pTree T)
{
    if(T == NULL)
        return;
    queue<pTree> Que;

    Que.push(T);

    while(!Que.empty())
    {

        pTree p = Que.front();
        cout << p->data << " ";
        Que.pop();

        if(p->Left != NULL)
            Que.push(p->Left);
        if(p->Right != NULL)
            Que.push(p->Right);

    }
}

整个测试程序如下

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <stack>
#include <queue>

using namespace std;

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef char ElementChar;
typedef int Status;

typedef struct TreeNode
{
    ElementChar data;
    struct TreeNode *Left;
    struct TreeNode *Right;
    bool  bMark;
}TreeNode, *pTree;

int index = 0;
char str[] = "ABDH#K###E##CFI###G#J##";



Status InitTree(pTree *T)
{
    *T = NULL;
    return OK;
}

Status Visit(pTree T)
{
    if(T == NULL)
        return ERROR;
    //printf("%c ",T->data);
    cout << T->data << " ";
    return OK;
}

int TreeIsEmpty(pTree T)
{
    if(T)
        return FALSE;
    else
        return TRUE;
}

void DeleteTree(pTree *T)
{
    if(*T)
    {
        if((*T)->Left)
            DeleteTree(&(*T)->Left);
        if((*T)->Right)
            DeleteTree(&(*T)->Right);
        free(*T);
        *T = NULL;
    }
}

void CreateTree(pTree *T)
{
    ElementChar ch;
    ch = str[index++];
    if(ch == '#')
        *T = NULL;
    else
    {
        *T = (pTree)malloc(sizeof(TreeNode));
        if((*T) == NULL)
            exit(0);
        (*T)->data = ch;
        CreateTree(&(*T)->Left);
        CreateTree(&(*T)->Right);
    }

}

void PreorderTraverse1(pTree T)
{
    if(T == NULL)
        return;
    cout << T->data << " ";
    PreorderTraverse1(T->Left);
    PreorderTraverse1(T->Right);
}

void InorderTraversel(pTree T)
{
    if(T == NULL)
        return;
    InorderTraversel(T->Left);
    cout << T->data << " ";
    InorderTraversel(T->Right);
}

void PostorderTraversel(pTree T)
{
    if(T == NULL)
        return;
    PostorderTraversel(T->Left);
    PostorderTraversel(T->Right);
    cout << T->data << " ";
}


void PreorderTraverse3(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;
    Stk.push(T);

    while(!Stk.empty())
    {
        pTree p = Stk.top();
        Stk.pop();
        cout << p->data << " ";
        if(p->Right != NULL)
            Stk.push(p->Right);
        if(p->Left != NULL)
            Stk.push(p->Left);
    }

}


/*preOrder2每次都将遇到的节点压入栈,当左子树遍历完毕后才从栈中弹出最后一个
访问的节点,访问其右子树。在同一层中,不可能同时有两个节点压入栈,因此栈的大小
空间为O(h),h为二叉树高度。时间方面,每个节点都被压入栈一次,弹出栈一次,访问
一次,复杂度为O(n)

*/

/*
前序是先访问,再入栈;

1、如果T非空,访问栈顶元素,把T入栈,T指向左儿子。。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,T指向右儿子。

*/

void PreorderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;


    while(T || !Stk.empty())
    {
        //如果T非空,访问栈顶元素,把T入栈,T指向左儿子。
        if(T)
        {
            cout << T->data << " ";
            Stk.push(T);
            T = T->Left;
        }
        //如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,T指向右儿子。
        else
        {
            T = Stk.top();
            Stk.pop();
            T = T->Right;
        }

    }


}

/*
中序则是先入栈,弹栈后再访问。

1、如果T非空,则把T入栈,T指向左儿子。
2、如果T为空,说明已经向左走到尽头了,弹出当前栈顶元素,进行访问,并把T指向其右儿子。
*/

void InorderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;


    while(T || !Stk.empty())
    {
        if(T)
        {
            Stk.push(T);
            T = T->Left;
        }

        else
        {
            T = Stk.top();
            Stk.pop();
            cout << T->data << " ";
            T = T->Right;
        }

    }
}

/*
因为后序遍历的顺序是:左子树->右子树->根节点,于是我们在前序遍历的代码中,
当访问完当前节点后,先把当前节点的左子树入栈,再把右子树入栈,这样最终得到
的顺序为:根节点->右子树->左子树,刚好是后序遍历倒过来的版本,于是把这个结
果做一次翻转即为真正的后序遍历。而翻转可以通过使用另外一个栈简单完成,这样的
代价是需要两个栈,但就复杂度而言,空间复杂度仍然是O(h)。
*/

void PostorderTraverse2(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk,Stk2;
    Stk.push(T);

    while(!Stk.empty())
    {
        pTree p = Stk.top();
        Stk.pop();
        Stk2.push(p);
        if(p->Left != NULL)
            Stk.push(p->Left);
        if(p->Right != NULL)
            Stk.push(p->Right);
    }

    while(!Stk2.empty())
    {
        cout << Stk2.top()->data << " ";
        Stk2.pop();
    }
}
void PostorderTraverse3(pTree T)
{
    if(T == NULL)
        return;
    stack<pTree> Stk;
    pTree Curr = T;
    pTree Previsited = NULL;

    while(Curr != NULL || !Stk.empty())
    {
        //一直访问左子树,直至为空
        while(Curr != NULL)
        {
            Stk.push(Curr);
            Curr = Curr->Left;
        }
        Curr = Stk.top();
        //当前节点的右子树不存在或已经访问过,则访问该节点
        if(Curr->Right == NULL || Curr->Right == Previsited)
        {
            cout << Curr->data << " ";
            Stk.pop();
            Previsited = Curr;
            Curr = NULL;
        }
        else
        {
            Curr = Curr->Right;
        }
    }
}

void LeverTraverse(pTree T)
{
    if(T == NULL)
        return;
    queue<pTree> Que;

    Que.push(T);

    while(!Que.empty())
    {

        pTree p = Que.front();
        cout << p->data << " ";
        Que.pop();

        if(p->Left != NULL)
            Que.push(p->Left);
        if(p->Right != NULL)
            Que.push(p->Right);

    }
}
int main()
{
    pTree Tree;

    InitTree(&Tree);

    CreateTree(&Tree);

    if(TreeIsEmpty(Tree))
    {
        cout << "Tree is Empty!" << endl;
    }
    cout << "前序遍历:" << endl;
    cout << "PreorderTraverse1(递归) is :";
    PreorderTraverse1(Tree);
    cout << endl;

    cout << "PreorderTraverse2       is :";
    PreorderTraverse2(Tree);
    cout << endl;

    cout << "PreorderTraverse3       is :";
    PreorderTraverse3(Tree);
    cout << endl;
    cout << endl;

    cout << "中序遍历:" << endl;
    cout << "InorderTraversel(递归)  is :";
    InorderTraversel(Tree);
    cout << endl;

    cout << "InorderTraverse2        is :";
    InorderTraverse2(Tree);
    cout << endl;
    cout << endl;

    cout << "后序遍历:" << endl;
    cout << "PostorderTraversel(递归)is :";
    PostorderTraversel(Tree);
    cout << endl;

    cout << "PostorderTraverse2(双栈)is :";
    PostorderTraverse2(Tree);
    cout << endl;

    cout << "PostorderTraverse3      is :";
    PostorderTraverse3(Tree);
    cout << endl;
    cout << endl;

    cout << "层序遍历:" << endl;
    cout << "LeverTraverse          is :";
    LeverTraverse(Tree);
    cout << endl;

    return 0;
}

运行结果如下:
这里写图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值