1. 程式人生 > >資料結構| |二叉樹的三種遍歷方式(遞迴&&非遞迴)

資料結構| |二叉樹的三種遍歷方式(遞迴&&非遞迴)

首先來寫一下遞迴的!

對於遞迴要將大問題轉化為小問題,並且要有一個結束的位置。
比如:要前序遍歷一個二叉樹,那就是先訪問根節點,然後在訪問根節點的左子樹,在訪問根節點的右子樹,而左子樹與右子樹,又可以變成訪問該節點和該結點的左子樹和右子樹。這就變成了一個遞迴的思想了。而終止條件就是:直到訪問到葉子節點。

//遞迴
//前序遍歷二叉樹(根節點 左子樹 右子樹)
void BinaryTreePrevOrder(BTNode* root)
{
    //終止條件
    if (root == NULL)
    {
        return;
    }

    //先訪問根節點,然後遞迴訪問根節點的左子樹和右子樹
printf("%c ", root->_data); BinaryTreePrevOrder(root->_left); BinaryTreePrevOrder(root->_right); } //中序遍歷二叉樹(左子樹 根節點 右子樹) void BinaryTreeInOrder(BTNode* root) { //終止條件 if (root == NULL) { return; } //先遞迴訪問左子樹,在訪問根節點,在遞迴訪問右子樹 BinaryTreeInOrder(root->
_left); printf("%c ", root->_data); BinaryTreeInOrder(root->_right); } //後序遍歷二叉樹(左子樹 右子樹 根節點) void BinaryTreePostOrder(BTNode* root) { //終止條件 if (root == NULL) { return; } //先遞迴訪問左子樹。在遞迴訪問右子樹,最後訪問根節點 BinaryTreePostOrder(root->_left); BinaryTreePostOrder(root->
_right); printf("%c ", root->_data); }

接下來再寫一下非遞迴的。(使用棧)
對於非遞迴的二叉樹的遍歷。

//非遞迴遍歷二叉樹
//前序遍歷(根節點  左子樹    右子樹)
//思路:先將該二叉樹的左路結點壓棧遍歷完,然後在訪問根節點,然後再對右結點進行剛才同樣的事情,
//進行左路結點壓棧遍歷,然後在訪問右結點,結束條件:該節點是葉子就不需要在進行壓棧,對其進行出棧就可以了
void BinaryTreePrevOrderNonR(BTNode* root)
{
    assert(root);

    Stack s;
    StackInit(&s);
    BTNode* cur = root;

    //StackEmpty必須存在因為在遍歷的過程中,cur有可能會等於NULL
    while (cur || StackEmpty(&s))
    {
        //將左路結點進行壓棧
        while (cur)
        {
            StackPush(&s, cur);
            printf("%c ", cur->_data);
            cur = cur->_left;
        }

        BTNode* top = StackTop(&s);
        StackPop(&s);

        cur = top->_right;
    }

    printf("\n");
}

//中序遍歷(左子樹  根節點   右子樹)
//思想:中序遍歷和前序遍歷基本一樣,只是在輸出結點資料的位置不同,中序遍歷是在對該節點進行出棧的時候,訪問該節點
void BinaryTreeInOrderNonR(BTNode* root)
{
    assert(root);

    Stack s;
    StackInit(&s);
    BTNode* cur = root;

    //StackEmpty必須存在因為在遍歷的過程中,cur有可能會等於NULL
    while (cur || StackEmpty(&s))
    {
        //將左路結點進行壓棧
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }

        BTNode* top = StackTop(&s);
        printf("%c ", top->_data);
        StackPop(&s);

        cur = top->_right;
    }

    printf("\n");
}

//後序遍歷(左子樹   右子樹   根節點)
//思想:先將左路結點全部入棧,然後出棧的時候
//如何判斷是否要訪問該結點,
//如果該節點的右結點為空的話,或者該結點的右結點等於剛訪問的上一個節點prev,那就直接訪問該節點
//否則就是該節點的右結點沒有進行訪問呢,那就訪問該節點的右結點
void BinaryTreePostOrderNonR(BTNode* root)
{
    assert(root);

    Stack s;
    StackInit(&s);
    //prev記錄的是出棧的結點
    BTNode* prev = NULL;
    BTNode* cur = root;

    //cur必須存在因為開始要從cur進入,只不過是在遍歷的時候,cur絕對不會等於NULL
    while (cur || StackEmpty(&s))
    {
        //將左路結點全部入棧
        while (cur)
        {
            StackPush(&s, cur);
            cur = cur->_left;
        }

        BTNode* top = StackTop(&s);
        //說明該節點的右結點已經訪問過了,可以進行輸出了
        if (top->_right == NULL || top->_right == prev)
        {
            StackPop(&s);
            prev = top;
            printf("%c ", top->_data);
        }
        //該節點的右結點沒有訪問過,那就繼續對右結點進行子迴圈
        else
        {
            cur = top->_right;
        }
    }
    printf("\n");
}