二叉樹 - 遍歷和存儲結構
在《二叉樹的定義和性質》中我們已經認識了二叉樹這種數據結構。我們知道鏈表的每個節點可以有一個後繼,而二叉樹(Binary Tree)的每個節點可以有兩個後繼。比如這樣定義二叉樹的節點:
typedef struct node *link;
struct node {
unsigned char item;
link l, r;
};
這樣的節點可以組織成下圖所示的形態。
二叉樹可以這樣遞歸地定義:
1. 就像鏈表有頭指針一樣,每個二叉樹都有一個根指針(上圖中的root指針)指向它。根指針可以是NULL,表示空二叉樹,或者
2. 根指針可以指向一個節點,這個節點除了有數據成員之外還有兩個指針域,這兩個指針域又分別是另外兩個二叉樹(左子樹和右子樹)的根指針。
鏈表的遍歷方法是顯而易見的:從前到後遍歷即可。二叉樹是一種樹狀結構,如何做到把所有節點都走一遍不重不漏呢?有以下幾種方法,如下圖(來自《linux c 編程一站式學習》)所示:
前序(Pre-order Traversal)(深度優先搜索)、中序(In-order
Traversal)、後序遍歷(Post-order Traversal)、層序遍歷(Level-order
Traversal)(廣度優先搜索)。如何分辨三種次序的遍歷方法呢?《data structrue and algorithm analysis
in c》中有一句:We
can evaluate an expression tree, T, by applying the operator at the
root to the values obtained by recursively evaluating the left and right
subtrees.
個人總結就是:前序 (root->left->right) ; 中序(left->root->right); 後序(left->right->root)
舉例上圖來說,前序遍歷,首先root是4,接著要Left,就是指左邊子樹,在左邊子樹中又先是root即2,然後是left的1,接著說right的3,現在左邊子樹遞歸完畢了,接著右邊子樹,同樣先root即5,沒有left,最後是right的6,所以最後排列是421356。
註意:已知前序遍歷序列和中序遍歷序列,可以唯一確定一棵二叉樹。
已知後序遍歷序列和中序遍歷序列,可以唯一確定一棵二叉樹。
但已知前序和後序遍歷序列,是不能確定一棵二叉樹的。
如果我們要在內存中建立一個如圖6-9-1左圖這樣的樹,為了能讓每個結點確認是否有左右孩子,可以對它進行擴展,如右圖那樣,也就是將二叉樹的每個結點的空指針引出一個虛結點,其值為一特定值,比如‘#‘。我們稱這種處理後的二叉樹為擴展二叉樹。擴展二叉樹就可以做到一個遍歷序列確定一棵二叉樹了。比如圖6-9-1的前序遍歷序列就為AB#D##C##。
示例程序如下:(改變自《大話數據結構》)
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
#include<iostream> using namespace std; #define MAXSIZE 50 typedef char ElemType; typedef char String[MAXSIZE + 1]; //以‘\0’結尾 String str; /* 用於構造二叉樹*/ ElemType NoChar = ‘ ‘; /* 字符型以空格符為空 */ /* 結點結構 */ typedef struct BTNode { ElemType data;/* 結點數據 */ struct BTNode *LChild;/* 左右孩子指針 */ struct BTNode *RChild; } BTNode, *BTNodePtr; /* 構造一個字符串 */ bool StrAssign(String Dest, char *ptr) { cout << "Assign Str ..." << endl; int i; for (i = 0; ptr[i] != ‘\0‘ && i < MAXSIZE; i++) Dest[i] = ptr[i]; Dest[i] = ‘\0‘; return true; } /* 構造空二叉樹 */ bool InitBTree(BTNodePtr *Tpp) { *Tpp = NULL; return true; } /* 銷毀二叉樹 */ void DestroyBTree(BTNodePtr *Tpp) { if (*Tpp) { if ((*Tpp)->LChild)/* 有左孩子 */ DestroyBTree(&(*Tpp)->LChild);/* 銷毀左孩子子樹 */ if ((*Tpp)->RChild)/* 有右孩子 */ DestroyBTree(&(*Tpp)->RChild); /* 銷毀右孩子子樹 */ free(*Tpp);/* 釋放根結點 */ *Tpp = NULL;/* 空指針賦0 */ } } /* 按前序輸入二叉樹中結點的值(一個字符) */ /* #表示空樹,構造二叉鏈表表示二叉樹。 */ void CreateBTree(BTNodePtr *Tpp) { ElemType ch; static int i = 0; if (str[i] != ‘\0‘) ch = str[i++]; if (ch == ‘#‘) *Tpp = NULL; else { *Tpp = (BTNodePtr)malloc(sizeof(BTNode)); if (!*Tpp) exit(1); (*Tpp)->data = ch;/* 生成根結點 */ CreateBTree(&(*Tpp)->LChild);/* 構造左子樹 */ CreateBTree(&(*Tpp)->RChild);/* 構造右子樹 */ } } bool BTreeEmpty(BTNodePtr Tp) { if (Tp) return false; else return true; } /*返回二叉樹的深度 */ int BTreeDepth(BTNodePtr Tp) { int i, j; if (!Tp) return 0; if (Tp->LChild) i = BTreeDepth(Tp->LChild); else i = 0; if (Tp->RChild) j = BTreeDepth(Tp->RChild); else j = 0; return i > j ? i + 1 : j + 1; } /* 返回根節點的數值 */ ElemType Root(BTNodePtr Tp) { if (BTreeEmpty(Tp)) return NoChar; else return Tp->data; } /* 前序遞歸遍歷*/ void PreOrderTraverse(BTNodePtr Tp) { if (Tp == NULL) return; cout << Tp->data << ‘ ‘; PreOrderTraverse(Tp->LChild); PreOrderTraverse(Tp->RChild); } /* 中序遞歸遍歷*/ void InOrderTraverse(BTNodePtr Tp) { if (Tp == NULL) return; InOrderTraverse(Tp->LChild); cout << Tp->data << ‘ ‘; InOrderTraverse(Tp->RChild); } /* 後序遞歸遍歷*/ void PostOrderTraverse(BTNodePtr Tp) { if (Tp == NULL) return; PostOrderTraverse(Tp->LChild); PostOrderTraverse(Tp->RChild); cout << Tp->data << ‘ ‘; } int main(void) { BTNodePtr Tp; InitBTree(&Tp); StrAssign(str, "ABDH#K###E##CFI###G#J##"); cout << "輸入字符序列(前序遍歷)為:" << endl; cout << str << endl; CreateBTree(&Tp); cout << "前序遍歷二叉樹:" << endl; PreOrderTraverse(Tp); cout << endl; cout << "中序遍歷二叉樹:" << endl; InOrderTraverse(Tp); cout << endl; cout << "後序遍歷二叉樹:" << endl; PostOrderTraverse(Tp); cout << endl; cout << "二叉樹的根節點為:" << Root(Tp) << endl; cout << "二叉樹的深度為:" << BTreeDepth(Tp) << endl; cout << "銷毀二叉樹 ..." << endl; DestroyBTree(&Tp); if (BTreeEmpty(Tp)) cout << "二叉樹現已為空..." << endl << endl; return 0; } |
輸出為:
二叉樹 - 遍歷和存儲結構