數據結構 --- 二叉樹的先序、中序和後序遍歷(非遞歸版本)

之前,我們寫過二叉樹的一些基本操作,大部分是通過遞歸來實現的。想想看,要是不採用遞歸的方式,我們該如何對二叉樹進行操作?
下面,我們用非遞歸方式完成二叉樹的先序、中序和後序遍歷。

1.通過循環實現樹的先序遍歷

還是我們的那顆二叉樹:
這裏寫圖片描述
我們先想一下,先序遍歷的順序是:根結點 —> 左子樹 —> 右子樹。只要我們先打印根結點,然後再打印根結點的左右子樹,每一個節點都這樣循環的進行,直到遍歷完所有的結點。

思路:這裏,我們建一個棧,先將根結點入棧,對照上圖,即 A 入棧,循環的取棧頂元素,出棧,並且訪問棧頂元素,即第一次打印 A ,然後先將該元素的右子樹入棧 即C,再將左子樹 B 入棧,重複上述步驟,直到棧爲空,說明遍歷結束。
代碼如下:


void TreePreOrderByLoop(TreeNode* root)
{
    if(root == NULL)
    {
        return;
    }
    //1.先把根節點入棧
    SeqStack stack;
    SeqStackInit(&stack);
    SeqStackPush(&stack,root);
    //2.循環的取棧頂元素,棧爲空,說明循環結束
    TreeNode* cur;
    while(1)
    {
        int ret = SeqStackTop(&stack,&cur);
        if(ret == 0){
            break;
        }
        //a)取棧頂元素爲當前元素;
        //b)出棧
        SeqStackPop(&stack);
        //c)訪問當前元素
        printf("%c ",cur->data);
        //d)將當前點的右子樹入棧
        if(cur->rchild != NULL)
        {
            SeqStackPush(&stack,cur->rchild);
        }
        //e)將當前點的左子樹入棧
        if(cur->lchild != NULL)
        {
            SeqStackPush(&stack,cur->lchild);
        }
    }
    printf("\n");
    return;
}

2.通過循環實現樹的中序遍歷

這裏寫圖片描述
思路:定義一個cur指針,先將cur 指向root,循環判斷cur是否爲空
如果不爲空,入棧,然後將cur指向cur的 lchild;
如果爲空,取棧頂元素,出棧並且訪問它,再將cur指向棧頂元素的 rchild。直到棧爲空,遍歷結束。
具體實現代碼如下:

void TreeInOrderByLoop(TreeNode* root)
{
    if(root == NULL)
    {
        return;//非法輸入
    }
    SeqStack stack;
    SeqStackInit(&stack);
    //1.先將cur指向root 
    TreeNode* cur = root;
    //2.循環的判斷cur是否爲空,
    while(1){
        while(cur != NULL)
        {
            //  如果不爲空,入棧,然後讓cur指向cur的lchild
            SeqStackPush(&stack,cur);
            cur = cur->lchild;
        }
        // 如果爲空,取棧頂元素,訪問出棧,cur指向cur的rchild
        TreeNode* top = NULL;
        int ret = SeqStackTop(&stack,&top);
        if(ret == 0)
        {
            return;//棧爲空,遍歷完成
        }
        printf("%c ",top->data);
        SeqStackPop(&stack);
        cur = top->rchild;
    }
}

3.通過循環實現樹的後序遍歷

思路:後序遍歷和中序遍歷思路大體一致,但有一個很重要的區別:當取到棧頂元素後我們不能立即訪問它,因爲不確定它的右子樹是否已經訪問過。(後序遍歷的順序:左子樹 —> 右子樹 —>根節點)。因此,訪問棧頂元素需要滿足兩個條件:1.當前節點右子樹爲空;2.右子樹已經訪問過了(即當前節點的上一個節點,後序遍歷根節點前一個就是右子樹)。
代碼如下:

void TreePostOrderByLoop(TreeNode* root)
{
    if(root == NULL)
    {
        return;//非法輸入
    }
    SeqStack stack;
    SeqStackInit(&stack);
    //1.先讓cur指向root
    TreeNode* cur = root;
    //pre保存上一個訪問過的元素
    TreeNode* pre = NULL;
    //2.循環的判斷cur是否爲空
    while(1){
        while(cur != NULL)
        {
            //3.如果不爲空,壓棧
            SeqStackPush(&stack,cur);
            //讓cur指向cur的左孩子結點
            pre = cur;
            cur = cur->lchild;
        }
        //4.如果爲空,循環取棧頂元素,對棧頂元素進行判定,
        TreeNode* top;
        int ret = SeqStackTop(&stack,&top);
        if(ret == 0)
        {
            //棧爲空,說明遍歷完成
            return;
        }
        //  a)如果棧頂元素的右子樹訪問過了,和訪問的上一個元素是同一個
        //  b)或者棧頂元素沒有右子樹
        //  就訪問棧頂元素,然後出棧
        if(top->rchild == NULL || top->rchild == pre)
        {
            printf("%c ",top->data);
            SeqStackPop(&stack);
            pre = top;
        }
        else
        {
            //5.如果不滿足以上條件,就讓cur指向棧頂元素的右子樹,重複循環
            cur = top->rchild;
        }
    }
    return;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章