1. 二叉樹的存儲結構
1.1 順序存儲結構
順序存儲一棵二叉樹時,首先對該樹中的每個結點進行編號,然后以各結點的編號為下標,把各結點的值對應存儲到一個一位數組中。每個結點的編號與等深度的滿二叉樹中對應結點的編號相等,即樹根結點的編號為1,接着按照從上到下和從左到右的次序,若一個結點的編號為i,則左、右孩子的編號分別為2i和2i+1。如圖,各結點上方的數字就是該結點的編號。
假設分別采用一維數組data1和data2來順序存儲上圖的兩棵二叉樹,則兩數組中各元素的值如下圖所示。
在二叉樹的順序存儲結構中,各結點之間的關系是通過下標計算出來的,因此訪問每一個結點的雙親和左、右孩子都非常方便。如對於編號為i的結點,其雙親結點的下標為⌊i/2⌋,若存在左孩子,則左孩子結點的下標為2i,若存在右孩子,則右孩子結點的下標為2i+1。
二叉樹的順序存儲結構對於存儲完全二叉樹是合適的,它能夠充分利用存儲空間,但對於一般二叉樹,特別是對於那些單支結點較多的二叉樹來說是很不合適的,因為可能只有少數存儲位置被利用,而多數或絕大多數的存儲位置空間着。
1.2 鏈接存儲結構
在二叉樹的鏈接存儲中,通常采用的方法是,在每個結點中設置3個域:值域、左指針域和右指針域。其結點結構為:
鏈接存儲的另一種方法是:在上面的結點結構中再增加一個parent指針域,用來指向其雙親結點。這種存儲結構既便於查找孩子結點,也便於查找雙親結點,當然也帶來存儲空間的相應增加。
同單鏈表相同,二叉鏈表既可由獨立分配的結點鏈接而成,也可由數組中的元素結點鏈接而成。
若采用獨立結點,則結點類型可定義為:
1 struct BTreeNode { 2 ElemType data; 3 BTreeNode* left; 4 BTreeNode* right; 5 };
若采用元素結點,則結點類型可定義為:
1 struct ABTreeNode { 2 ElemType data; 3 int left, right; 4 };
元素結點從下標為1的位置起使用,下標為0的位置的左指針域通常用來存儲樹根指針,右指針域通常用來存儲空閑鏈表的表頭指針,空閑鏈表由空閑結點的right域鏈接而成。
2. 二叉樹遍歷
二叉樹遍歷算法有:前序遍歷、中序遍歷、后序遍歷和按層遍歷。
若將跟結點、左子樹和右子樹分別用D、L和R表述,則前序遍歷順序為DLR,中序遍歷順序為LDR,后序遍歷順序為LRD。
對於上圖二叉樹,
前序遍歷為:ABCDEFG
中序遍歷為:CBDAEGF
后序遍歷為:CDBGFEA
按層遍歷為:ABECDFG
2.1 前序遍歷算法
1 void PreOrder(BTreeNode* BT) { 2 if(BT != NULL) { 3 cout << BT->data << ' '; //訪問根結點 4 PreOrder(BT->left); //前序遍歷左子樹 5 PreOrder(BT->right); //前序遍歷右子樹 6 } 7 }
2.2 中序遍歷算法
1 void InOrder(BTreeNode* BT) { 2 if(BT != NULL) { 3 InOrder(BT->left); //中序遍歷左子樹 4 cout << BT->data << ' '; //訪問根結點 5 InOrder(BT->right); //中序遍歷右子樹 6 } 7 }
2.3 后序遍歷算法
1 void PostOrder(BTreeNode* BT) { 2 if(BT != NULL) { 3 PostOrder(BT->left); //后序遍歷左子樹 4 PostOrder(BT->right); //后序遍歷右子樹 5 cout << BT->data << ' '; //訪問根結點 6 } 7 }