1. 程式人生 > >二叉樹(建立與訪問)(先序,中序,後序)

二叉樹(建立與訪問)(先序,中序,後序)

二叉樹的建立(先序建立)

二叉樹的訪問(遞迴與非遞迴 先序)(遞迴與非遞迴中序)(遞迴與非遞迴 後序)

#include<iostream>
#include<stack>
using namespace std;

struct Tree_Node ///結點的結構
{
    char data; ///每個結點的資料
    Tree_Node * left; ///左子樹
    Tree_Node * right; ///右孩子
};
///按照先序遍歷的方式,構建我們的二叉樹,輸入的時候,
///我們要按照完全二叉樹的形式輸入,結點為空的位置,輸入“#”
void createTree(Tree_Node *&t)
{
    char str;
    cin>>str;
    if(str=='#')
    {
        t=NULL;
    }
    else
    {
        t=new Tree_Node; ///為t開闢空間
        t->data = str;
        createTree(t->left);
        createTree(t->right);
    }
}
void PreorderTraverse(Tree_Node * T)///先序遍歷,遞迴實現
{
    if(T)
    {
        if(T->data!='#') cout<<T->data<<" ";   ///訪問根結點
        PreorderTraverse(T->left);  ///訪問左子樹
        PreorderTraverse(T->right);  ///訪問右子樹
    }
}
///非遞迴實現,思路:
///首先我們的迴圈條件是:結點不為空或者棧不為空。
///然後是先把根結點加入桟中,然後,遍歷左子樹,當左子樹遍歷完後,棧頂元素為剛剛的根結點,
///然後,讓根結點出棧,遍歷右子樹
void PreorderTraverse_no_recursive(Tree_Node * T)
{
    stack<Tree_Node*> s;
    Tree_Node *p = T; ///棧不為空或者T不為空時,迴圈繼續
    while(p || !s.empty())
    {
        if(p!=NULL)
        {
            s.push(p); ///根結點入棧
            if(p->data!='#')///訪問根結點
            cout<<p->data<<' ';
            p = p->left; ///先遍歷左子樹
        }
        else
        {
            p = s.top(); ///根結點出棧
            s.pop();
            p = p->right;///遍歷右子樹
        }
    }
}
void InorderTraverse(Tree_Node * T)///遞迴實現中序遍歷
{
    if(T)
    {
        InorderTraverse(T->left); ///中序遍歷左孩子
        if(T->data!='#') cout<<T->data<<" ";       ///訪問根結點
        InorderTraverse(T->right); ///中序遍歷右孩子
    }
}
///非遞迴實現中序遍歷,思路:
///思路:T是要遍歷樹的根指標,中序遍歷要求在遍歷完左子樹後,訪問根,再遍歷右子樹。
///先將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應為T,出棧,訪問T->data,
///再中序遍歷T的右子樹。
void InorderTraverse_recursive(Tree_Node * T)
{
    stack<Tree_Node*> s;
    Tree_Node * p=T;   ///棧不為空或者T不為空時,迴圈繼續
    while(p || !s.empty())
    {
        if(p!=NULL)
        {
            s.push(p); ///根結點入棧
            p = p->left; ///先遍歷左子樹
        }
        else
        {
            p=s.top(); ///根結點出棧
            s.pop();
            if(p->data!='#') cout<<p->data<<' ';///訪問根結點
            p = p->right;///遍歷右子樹
        }
    }
}
void PostorderTraverse(Tree_Node * T)///遞迴實現後序遍歷
{
    if(T)
    {
        PostorderTraverse(T->left); ///訪問左子樹
        PostorderTraverse(T->right); ///訪問右子樹
        if(T->data!='#') cout<<T->data<<" "; ///訪問根結點
    }
}
///非遞迴實現後序遍歷
///思路:我們要保證根結點在左孩子和右孩子訪問之後才能訪問,因此對於任一結點P,先將其入棧。
///如果P不存在左孩子和右孩子,則可以直接訪問它;
///或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了,則同樣可以直接訪問該結點。
///若非上述兩種情況,則將P的右孩子和左孩子依次入棧,這樣就保證了每次取棧頂元素的時候,
///左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。
void PostorderTraverse_recursive(Tree_Node * T)
{
    Tree_Node *pre; ///前一個被訪問的結點。
    pre=NULL;
    stack<Tree_Node*> s;
    Tree_Node *cur;  ///記錄棧頂的結點,
    s.push(T); ///先把根結點入棧
    while(!s.empty())
    {
        cur=s.top(); ///cur記錄的是棧頂的結點
        if((cur->left==NULL && cur->right==NULL) || (pre!=NULL && (pre==cur->left || pre==cur->right)))
        {
            cout<<cur->data<<" "; ///滿足:不存在左孩子和右孩子;或者存在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了
            s.pop();
            pre=cur; ///更新pre的值
        }
        else
        {
            if(cur->right!=NULL) s.push(cur->right); ///一定是右子樹先入棧的,因為這樣才可以比左子樹後被訪問
            if(cur->left!=NULL)s.push(cur->left); ///左子樹入棧

        }
    }
}
int main()
{
    Tree_Node * T;
    createTree(T);
    cout<<"先序遍歷--遞迴實現"<<endl;
    PreorderTraverse(T);
    cout<<endl;
    cout<<"先序遍歷--非遞迴實現"<<endl;
    PreorderTraverse_no_recursive(T);
    cout<<endl;
    cout<<"中序遍歷--遞迴實現"<<endl;
    InorderTraverse(T);
    cout<<endl;
    cout<<"中序遍歷--非遞迴實現"<<endl;
    InorderTraverse_recursive(T);
    cout<<endl;
    cout<<"後序遍歷--遞迴實現"<<endl;
    PostorderTraverse(T);
    cout<<endl;
    cout<<"後序遍歷--非遞迴實現"<<endl;
    PostorderTraverse_recursive(T);
    cout<<endl;
    return 0;
}