1. 程式人生 > >二叉樹先序、中序、後序遞迴&&非遞迴

二叉樹先序、中序、後序遞迴&&非遞迴

這三種遍歷,遞迴方式都很簡單,大概就圍繞著這樣一個順序:

先序:根左右,中序:左根右,後序:左右根。

對於非遞迴方式:由於採用棧(先進後出)這種資料結構儲存結點,因此,結點入棧時需要考慮順序和原來相反。

先序和中序都比較簡單,後序相對較難,在程式碼中敘述。

先序:首先根結點入棧,訪問根結點,然後結點入棧就先入右邊,再入左邊,因為退棧訪問時候是先進後出的,而先序順序是按照根左右這樣一個順序,最後就一直按照這樣退棧訪問再入棧即可。

中序:先入棧根結點,然後讓指標一直沿著左子樹走到最左下,途中將所有遇到的非空左子結點入棧,然後退棧訪問,退棧時棧頂結點的右子結點入棧,繼續上述操作即可。

/*
在後序遍歷中,要保證左孩子和右孩子都已被訪問並且左孩子在右孩子前訪問才能訪問根結點
思路:對於任一結點P,將其入棧,然後沿其左子樹一直往下搜尋,直到搜尋到沒有左孩子的結點,
此時該結點出現在棧頂,但是此時不能將其出棧並訪問, 因此其右孩子還未被訪問。所以接下來
按照相同的規則對其右子樹進行相同的處理,當訪問完其右孩子時,該結點又出現在棧頂,此時可
以將其出棧並訪問。這樣就保證了正確的訪問順序。可以看出,在這個過程中,每個結點都兩次出
現在棧頂,只有在第二次出現在棧頂時,能訪問它。因此需要多設定一個變數標識該結點是否是第
一次出現在棧頂,或者用一個指標看該節點是否被訪問過。
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
typedef long long LL;
typedef struct node
{
    struct node *lchild,*rchild;
    int val;
}node,*BiTree;
BiTree Creat(BiTree &T)//先序遞迴建立,根左右
{
    int val;
    scanf("%d",&val);
    if(val==0) T=NULL;
    else
    {
        T=(BiTree)malloc(sizeof(node));
        T->val=val;
        Creat(T->lchild);
        Creat(T->rchild);
    }
    return T;
}
void print_mid(BiTree T)//中序遞迴程式碼,左根右
{
    if(T==NULL) return;
    print_mid(T->lchild);
    printf("%d ",T->val);
    print_mid(T->rchild);
}
void print_post(BiTree T)//後序遞迴程式碼,左右根
{
    if(T==NULL) return;
    print_post(T->lchild);
    print_post(T->rchild);
    printf("%d ",T->val);
}
void Pre_Order(BiTree T)//先序非遞迴
{
    stack<BiTree>s;
    BiTree p=T;
    s.push(p);
    while(!s.empty())
    {
        p=s.top();
        s.pop();
        cout<<p->val<<" ";
        if(p->rchild) s.push(p->rchild);
        if(p->lchild) s.push(p->lchild);
    }
}
void Mid_Order(BiTree T)//中序非遞迴
{
    stack<BiTree>s;
    BiTree p=T;
    while(p||!s.empty())
    {
        if(p)
        {
            s.push(p);
            p=p->lchild;
        }
        else
        {
            p=s.top();
            s.pop();
            cout<<p->val<<" ";
            p=p->rchild;
        }
    }
}
void Post_Order(BiTree T)//後序非遞迴
{
    stack<BiTree>s;
    BiTree p=T,r=NULL;
    //r用於指向最近訪問過的結點,也可以在結點中增加一個標記,記錄該結點是否被訪問
    while(p||!s.empty())
    {
        if(p)
        {
            s.push(p);
            p=p->lchild;//先沿著左子樹一直向下搜尋
        }
        else
        {
            p=s.top();//取棧頂元素
            if(p->rchild&&p->rchild!=r)//有右子樹且該節點第一次訪問
            {
                p=p->rchild;
                s.push(p);
                p=p->lchild;
            }
            else//無右子樹則將該結點出棧
            {
                p=s.top();
                s.pop();
                cout<<p->val<<" ";
                r=p;
                p=NULL;//結點訪問完之後重置p指標
            }
        }
    }
}
int main()
{
    printf("先序輸入一棵二叉樹:\n");
    BiTree T=Creat(T);
    printf("中序遞迴輸出二叉樹序列:\n");
    print_mid(T);
    printf("\n後序遞迴輸出二叉樹序列:\n");
    print_post(T);
    printf("\n先序非遞迴輸出二叉樹序列:\n");
    Pre_Order(T);
    printf("\n中序非遞迴輸出二叉樹序列:\n");
    Mid_Order(T);
    printf("\n後序非遞迴輸出二叉樹序列:\n");
    Post_Order(T);
    return 0;
}
//測試資料:1 2 3 5 0 0 0 4 0 0 2 4 0 0 3 0 5 0 0