看到一个非递归交换一个二叉树的左右孩子的位置,于是想实现之,才发现非递归的先中后序遍历都忘记了……于是杂七杂八的写了一些,抄抄资料就实现了,然后实现非递归交换两个孩子的位置还是相当容易的。先直接上代码吧,其实这东西还是得自己写写过一遍的,印象才会更加深刻:

#include <iostream>
#include <fstream>
#include <string>
#include <stack>

using std::cout;
using std::endl;
using std::cin;
using std::ifstream;
using std::stack;

class BTree
{
private:

    struct Node
    {
        Node* lChild;
        char data;
        Node* rChild;
    };

    Node* m_pRoot;        //pointer to tree root

    void addNode(ifstream& fin, Node** ppNode);
    void showNode(Node* pNode);        //递归中序遍历
    void switchLRChild(Node* pNode);

public:
    explicit BTree(void) { m_pRoot = nullptr;}
    void create(void);
    void show(void);        //这是中序遍历
    void switchLR(void);
    void switchLR2(void);        //非递归实现交换节点的左右孩子
    void preOrder(void);        //写着玩的非递归先序遍历
    void inOrder(void);        //写着玩的非递归中序遍历
    void aftOrder(void);        //写着玩的非递归后序遍历
};

void BTree::create(void)
{
    ifstream fin("src.txt");
    if(!fin)
        cout<<"can not open the file!\n";

    addNode(fin, &m_pRoot);
}

void BTree::addNode(ifstream& fin, Node** ppNode)
{
    /* 手工读取
    cout<<"Input node's value, blank to end:";

    char ch;
    cin.get(ch);
    if(ch == '\n')
        return;
    while(cin.get() != '\n')
        continue;

    (*ppNode) = new Node;
    (*ppNode)->data = ch;
    (*ppNode)->lChild = nullptr;
    (*ppNode)->rChild = nullptr;

    cout<<"add left child:"<<endl;
    addNode(&((*ppNode)->lChild));
    cout<<"add right child:"<<endl;
    addNode(&((*ppNode)->rChild));*/

    //文件读取
    std::string temp;
    getline(fin, temp);        //这个函数以换行为标志读取一行,但是会丢弃换行符
    if(temp.empty())
        return;

    (*ppNode) = new Node;
    (*ppNode)->data = temp[0];
    (*ppNode)->lChild = nullptr;
    (*ppNode)->rChild = nullptr;

    addNode(fin, &((*ppNode)->lChild));
    addNode(fin, &((*ppNode)->rChild));
}

void BTree::show(void)
{
    if(m_pRoot)
    {
        showNode(m_pRoot);
        cout<<endl;
    }
    else
    {
        cout<<"Empty tree!"<<endl;
    }
}

void BTree::showNode(Node* pNode)
{
    if (pNode)
    {
        showNode(pNode->lChild);
        cout<<pNode->data<<" ";
        showNode(pNode->rChild);
    }
    else
    {
        return;
    }
}

void BTree::switchLR(void)
{
    switchLRChild(m_pRoot);
}

void BTree::switchLRChild(Node* pNode)
{
    if (pNode && (pNode->lChild != nullptr || pNode->rChild != nullptr))
    {
        Node* temp = pNode->lChild;
        pNode->lChild = pNode->rChild;
        pNode->rChild = temp;

        switchLRChild(pNode->lChild);
        switchLRChild(pNode->rChild);
    }
}

void BTree::switchLR2(void)
{
    struct BNode
    {
        Node* pToNode;
        bool isFirst;
    };

    BNode p = {m_pRoot, true};
    stack<BNode> myStack;

    while(p.pToNode || !myStack.empty())
    {
        while(p.pToNode)
        {
            myStack.push(p);

            p.pToNode = p.pToNode->lChild;
            p.isFirst = true;    //重置
        }

        p = myStack.top();
        myStack.pop();

        if (p.isFirst)
        {
            p.isFirst = false;
            myStack.push(p);

            p.pToNode = p.pToNode->rChild;
            p.isFirst = true;
        } 
        else        //节点第二次在栈顶了,那么此时交换它的左右节点
        {
            Node* temp = p.pToNode->lChild;
            p.pToNode->lChild = p.pToNode->rChild;
            p.pToNode->rChild = temp;

            p.pToNode = nullptr;
        }
    }
    /*
    这个和非递归的后序遍历的原理一样:先直接沿着左侧一路向下,把遇到的每个节点都入栈,直到没有了。
    然后呢对于栈顶的节点,如果它是第一个出现在栈顶,那么还没有对它的右子树进行处理,所以切换到它的右子树
    进行同样的处理。当它的右子树处理完了之后这个节点再次出现在栈顶,这个时候就可以交换它的左右子树了,
    并且它的左右子树都已经完成了交换,这不就是递归吗?只不过是循环实现的就是啦。
    */
}

void BTree::preOrder(void)
{
    stack<Node*> myStack;
    Node* p = m_pRoot;

    while(p != nullptr || !myStack.empty())
    {
        while(p != nullptr)        //写的时候先写内层循环,再写外层循环
        {
            cout<<p->data<<" ";        //先访问数据
            myStack.push(p);        //指针入栈
            p = p->lChild;        //指向左孩子
        }
        if (!myStack.empty())    //到这里指针p肯定是空了
        {
            p = myStack.top();        //节点出现在栈顶时,此节点已经访问过,并且其左孩子也被访问过,只有其右孩子还没有
            myStack.pop();
            p = p->rChild;
        }
    }

    cout<<endl;
}

void BTree::inOrder(void)
{
    stack<Node*> myStack;
    Node* p = m_pRoot;

    while(p || !myStack.empty())    //外层循环相当于实现了递归的作用
    {
        while(p)
        {
            myStack.push(p);
            p = p->lChild;
        }
        if (!myStack.empty())
        {
            p = myStack.top();        //节点出现在栈顶,则其左孩子已经被访问过,自己和右孩子都没有被访问过
            myStack.pop();
            cout<<p->data<<" ";        //访问数据
            p = p->rChild;        //对右子树也用同样的方法,这不是递归的思想吗?
        }
    }

    cout<<endl;
}

void BTree::aftOrder(void)
{
    struct BNode        //这个结构体只多了一个是否是第一次访问的标志
    {
        Node* pToNode;
        bool isFirst;
    };

    BNode p;
    p.pToNode = m_pRoot;
    p.isFirst = true;

    stack<BNode> myStack;
    while(p.pToNode || !myStack.empty())
    {
        while(p.pToNode)
        {
            myStack.push(p);

            p.pToNode = p.pToNode->lChild;
            p.isFirst = true;    //注意这里每次切换的时候都要设置true或false,因为就那么一个变量,虽然放到栈里面都有true或false
        }

        if (!myStack.empty())
        {
            p = myStack.top();
            myStack.pop();

            if (p.isFirst)        //是第一次出现在栈顶
            {
                p.isFirst = false;
                myStack.push(p);

                p.pToNode = p.pToNode->rChild;
                p.isFirst = true;
            }
            else     //第二次出现在栈顶了
            {
                cout<<p.pToNode->data<<" ";
                p.pToNode = nullptr;        //这里其实就是让程序直接运行到下次出栈
            }
        }
    }

    cout<<endl;

    /*
    非递归后序遍历:先一直沿着左孩子向下,并把指向节点的指针入栈,直到不能继续向下了。
    这个时候对于栈顶的节点,让它出栈,但我们还不能访问它,如果这个时候访问了那不就是中序遍历了吗?
    也就是一个节点第一次出现在栈顶时不能访问它,这时要继续对它的右子树进行访问。
    当结束了对它的右子树访问时,这个时候这个节点又出现在了栈的顶端,这个时候才可以访问它,这才是后序遍历。
    */
}

int main(void)
{
    BTree myT;
    myT.create();
    myT.show();
    //myT.preOrder();
//    myT.switchLR();
//    myT.show();
    //myT.inOrder();
    //myT.aftOrder();
    myT.switchLR2();
    myT.show();

    cin.get();
}
source code
 1 C
 2 T
 3 G
 4 V
 5 
 6 
 7 
 8 W
 9 
10 
11 M
12 S
13 
14 
15 N
View Code

注意:上面的第二份是数据,即先序遍历这个树所经过的节点顺序,总共17行的,但这里只有15行,不关我的事啊,我都Ctrl+A、Ctrl+C了,后面两行是是两个换行符,用来表示读入树结束。




 

  首先是实现它的非递归的三种遍历啦,这个都是抄袭别人的啦,见附,这位博主确实写得好,排版还很漂亮,这个有加分的。

  二叉树的非递归先序和中序遍历算容易的了,我用自己的语言写出来:从树根沿着左边的方向一直下,并入栈,如果是先序遍历的话还要在入栈前先访问一下;到了最左下角了,栈顶出栈,这是大概意思就是刚出栈节点的左孩子已经访问完了,看你怎么办。如果是中序遍历,这时候当然要访问它的数据啦,然后接着看刚出栈节点的右孩子,其实这里不就是递归的思想吗?直接对它的的右孩子同样处理不就可以了吗?

  上面的思路已经很清晰了,转化为程序呢?先一个循环到最左下角,然后根据情况处理栈顶的东西。然后让指针指向栈顶的右孩子“递归”处理,这个“递归”还不能用递归,那也用循环代替啦:

//先序非递归大致结构:
指针P = 树根;
while (还没结束)
{
    while(还没到最左下角)
    {
        访问P->数据;
        P入栈;
        p = p的左孩子;
    }

    p = 出栈;
    p = p ->右孩子;        //然后继续循环啦
}

//中序非递归大致结构
指针P = 树根;
while (还没结束)
{
    while(还没到最左下角)
    {
        P入栈;
        p = p的左孩子;
    }

    p = 出栈;
    访问P->数据;
    p = p ->右孩子;        //然后继续循环啦
}

  下面是非递归实现后序遍历,详细的解释源代码中有注释:

//先序非递归大致结构:
指针P = 树根;
while (还没结束)
{
    while(还没到最左下角)
    {
        P入栈;
        p = p的左孩子;
    }

    p = 出栈;
    if (P指向的节点是第一次出现在栈顶)        //表明它的右孩子还没有搞过,得先等等
    {
        设置P指向的标志位是访问过;
        P入栈;
        p = p->右孩子;
    } 
    else
    {
        访问P->数据;
        设置P为空以使下次循环直接让栈顶出栈;
    }
}



  

  这里面最漂亮的是什么呢?就是双重循环实现了类似递归的思想,虽然不是直接的递归调用。因为二叉树本身的定义就是递归定义的嘛,什么根节点左右两个子树,子树都是二叉树什么的,既然定义都是递归的,那么它的很多操作当然可以递归实现啦。

  第二个问题不用解决了。

附:

http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html

posted on 2014-07-23 17:47  简单的信仰  阅读(1570)  评论(0编辑  收藏  举报