二叉樹基本操作:前序、中序、后序遍歷(遞歸方式)


  二叉樹是最常見最重要的數據結構之一,它的定義如下:

  二叉樹(binary tree)是有限多個節點的集合,這個結合或者是空集,或者由一個根節點和兩顆互不相交的、分別稱為左子樹和右子樹的二叉樹組成。

  二叉樹最基本的操作是遍歷:一般約定遍歷時左節點優先於右節點,這樣根據根節點的遍歷順序可分為三種遍歷操作:前序-先遍歷根節點,再處理左右節點;中序-先遍歷左節點,然后處理根節點,最后處理右節點;后序-先遍歷左右節點,然后處理根節點。

  從上邊二叉樹定義可以看出:二叉樹使用了遞歸的概念描述。所以,二叉樹的很多操作都可以很方便的通過遞歸來實現,當中當然包裹遍歷操作。實際上,三種遍歷操作的遞歸實現,是二叉樹其他大多數操作實現的基礎:絕大多數其他操作都可以在三種遍歷中的一種的基礎上變化而來。

  二叉樹通常有兩種存儲方式:順序存儲和鏈式存儲。本文代碼基於鏈式存儲方式實現,鏈式存儲節點定義如下:

typedef struct node *tree_pointer;
typedef struct node {
    //為了簡化,節點數據用整型
    int data;
    //指向當前節點左兒子和右兒子的指針
    tree_pointer left_child, right_child;
};

給出的節點定義中只有指向當前節點的左兒子和右兒子的指針,如果需要方便的知道當前的節點的父節點,可以在定義中增加指向父節點的指針: tree_pointer parent;

  有了節點的定義,就可以編寫二叉樹的遍歷函數,我們先給出遞歸中序遍歷函數:

1 void inorder(tree_pointer ptr)
2 {
3     if (ptr) {
4         inorder(ptr->left_child);
5         printf("\t%d", ptr->data);
6         inorder(ptr->right_child);
7     }
8 }

函數本身非常簡潔,下面我們以圖示的二叉樹解釋函數的遍歷過程:

如上圖所示,這顆二叉樹有九個節點:ABCDEFGHI,紅線連接的“節點”表示葉子節點的“左右兒子”(實際上葉子節點沒有左右兒子,這里便於描述,假設存在這些NULL節點)

根據中序遍歷的定義,我們可以得到這顆二叉樹的中序遍歷結果為:HFIDGBEAC.

把這顆二叉樹的root節點指針作為參數傳遞給inorder函數,則函數的執行過程如下:

調用函數: inorder(root);

(下面步驟號為調用inorder函數的順序次數)

1. 第一次調用inorder,ptr指向A,函數進入if結構,執行第四行代碼,第二次調用inorder,參數為A的左兒子B;

2. ptr->B,進入if,執行line4,第三次調用inorder: inorder(B->left_child);

3. ptr->D,進入if,line4,第四次調用:inorder(D->l_c);

4. ptr->F, if, line4: inorder(F->l_c);

5. ptr->H, if, line4: inorder(H->lc);

6. ptr->NULL, 沒有進入if, return;

5. 執行line5,printf(H),執行line6: inorder(H->rc);

7. ptr->NULL, 沒有進入if, return回 5 ,5已經執行完畢,return 回4;

4. line5,printf(F),line6: inorder(F->rc);

8. ptr->I, if, line4: inorder(I->lc);

9. ptr->NULL, return-8;

8. line5, printf(I), line6: inorder(I->rc);

10. ptr->NULL, return-8, 8 完成,return-4, 4完成, return-3;

3. line5:printf(D), line6: inorder(D->rc);

11. ptr->G, line4: inorder(G->lc);

12. ptr->NULL, return-11;

11. line5: printf(G), line6: inorder(G->rc);

13. ptr->NULL, return-11, 11完, return-3, 3完,return-2;

2. line5:printf(B), line6:inorder(B->rc);

14. ptr->E, line4: inorder(E->lc);

15. ptr->NULL, return-14;

14. line5: printf(E), line6: inorder(E->rc);

16. ptr->NULL, return-14, 14 finished, return-2, 2 finished, return 1;

1. line5: printf(A), line6: inorder(A->rc);

17. ptr->C, line4: inorder(C->lc) ;

18. ptr->NULL, return-17;

17. line5: printf(C); line6: inorder(C->rc);

19. ptr->NULL, return-17, 17 finished, return 1, 1 finished ,return 回調用函數

完成。

  上面的步驟手動描述了遞歸中序遍歷的執行過程,其中暗含了遞歸調用的入棧出棧過程。

  前序和后序的遞歸遍歷與中序類似,代碼實現如下:

//前序遍歷
void preorder(tree_pointer ptr)
{
    if (ptr) {
        printf("\t%d", ptr->data);
        preorder(ptr->left_child);
        preorder(ptr->right_child);
    }
}

//后序遍歷
void postorder(tree_pointer ptr)
{
    if (ptr) {
        postorder(ptr->left_child);
        postorder(ptr->right_child);
        printf("\t%d", ptr->data);
    }
}

函數的執行過程也與中序基本一樣,不再描述。

  (在下一篇文章中將給出這些遍歷函數的完整測試代碼) 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM