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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
| 一、先序遍历(根 -- 左子树 -- 右子树)
方法一:其实现思想为:
1. 访问节点 N ,并打印数据,然后将该节点入栈;
2. 判断该节点的左孩子是否为空,不为空则将该节点置为节点 N ,若为空,则取出栈顶节点并出栈操作,然后将该节点的右孩子置为节点 N ,循环 1-2; 3. 直到节点 N 为空或是栈中元素为空,则遍历完成。
void printPreorder(BinaryTree *pBinaryTree) { if (NULL == pBinaryTree) return ;
stack<BinaryTree*> S; BinaryTree *pS = pBinaryTree;
while ((pS != NULL) || !S.empty()) { while (pS) { cout << pS->value << endl; S.push(pS); pS = pS->left_child; }
if (!S.empty()) { pS = S.top(); S.pop(); pS = pS->right_child; }
} } 方法二:
1. 将根节点压栈;
2. 取出位于栈顶的节点 N 并打印数据,然后出栈操作;
3. 如果节点 N 有右孩子,则将右孩子节点压栈,随后如果节点 N 有左孩子,则将左孩子节点压栈;
4. 最后在栈不为空的情况下,循环 2-4。 void printPreorder(BinaryTree *pBinaryTree) { if (NULL == pBinaryTree) return;
stack<BinaryTree*> S; BinaryTree *pS = pBinaryTree;
S.push(pS);
while (!S.empty()) { pS = S.top(); cout << pS->value << endl; S.pop();
if (pS->right_child) S.push(pS->right_child); if (pS->left_child) S.push(pS->left_child);
} } 二、中序遍历(左子树 -- 根 -- 右子树)
方法一:实现思想:
1. 将节点 N 压栈,如果该节点存在左孩子则将其置为 N,重复操作;(此时栈中均为左孩子节点(栈底根节点))
2. 如果 N 节点不存在左孩子,则取出栈顶元素并出栈操作,如果N节点有右孩子则将其置为N,并压栈处理;
3. 循环1-2,直至N为空或栈为空。 void printInorder(BinaryTree *pBinaryTree) { if (NULL == pBinaryTree) return;
stack<BinaryTree*> S; BinaryTree *pS = pBinaryTree;
while ((pS != NULL) || !S.empty()) { while (pS) { S.push(pS); pS = pS->left_child; }
pS = S.top(); cout << pS->value << endl; if (!S.empty()) { S.pop(); pS = pS->right_child; }
} } 方法二: 中序遍历的特点是根节点(父节点)是在中间段访问,就是先访问左孩子节点,那么压栈顺序便是右孩子先入栈然后父节点,最后就是左节点。但必须保证每次入栈的顺序都是一样的(右-根-左),上一级的孩子节点,到了下一层就成了父节点,那么上一层压入的节点又得弹出来,然后等到其右孩子节点入栈后,再次入栈,然后压入其左孩子节点,如此循环,期间父节点(这里叶子节点也看作是特殊的父节点,因为其也存在访问其左右孩子的过程)必须有两次入栈过程,所以设定标记记录节点入栈的次数情况。
void printInorder(BinaryTree *pBinaryTree) { if (NULL == pBinaryTree) return;
stack<pair<BinaryTree*, bool>> S; BinaryTree *pS; bool isFirst; S.push(make_pair(pBinaryTree, true)); while (!S.empty()) { pS = S.top().first; isFirst = S.top().second; S.pop(); if (isFirst) { if (pS->right_child) S.push(make_pair(pS->right_child, true)); S.push(make_pair(pS, false)); if (pS->left_child) S.push(make_pair(pS->left_child, true)); } else { cout << pS->value << endl; } }
} 三、后序遍历(左子树 -- 右子树 -- 根) 后序遍历和其余两种遍历方式有所不同,因为根节点是最后访问的。
方法一:
先访问左子树,这就需要先把节点的左孩子入栈(循环),直到节点没有左孩子(栈中元素6,2,1),然后同样方式访问该节点的右孩子(右子树),如果没有右孩子时可取栈顶元素并出栈(栈中元素6,2),然后取栈顶元素2,同样方式访问节点2的右孩子,但是前面取栈顶元素2的时候却不能将节点2出栈,因为该节点的右子树还未访问。同样是栈顶元素那什么时候可以出栈呢?节点没有右孩子的时候么,如果这样的话那么节点2什么时候出栈勒。事实是当该节点两次出现(不是第一次)在栈顶的时候,因为鉴于后序遍历的特点(左-右-根),这样访问父节点的前提是该父节点的左孩子和右孩子均已访问完毕,访问完左孩子节点后(左孩子及节点出栈),父节点出现在栈顶,再转向访问右孩子,访问完毕后(右孩子节点出栈),这样父节点再一次出现在栈顶,这时便可以访问了,访问完后,该父节点也就可以出栈了。 所以我们就需要一个变量来记录某元素出现在栈顶的次数是否是第一次,当不是第一次出现时,表明该节点的左右孩子均已访问完毕(叶子节点虽然没有孩子,但也存在一个去访问其 “孩子” 的过程),这里遍历的是整型数据,不能像本专栏中第一篇博文介绍的那样判断唯一性方法来处理,这里定义一个结构体,将节点指针和记录该节点出现栈顶次数是否为一次的bool型变量绑在一起,可以在访问节点的时候进行判断。
typedef struct _BTnode { struct _BinaryTree *pTnode; bool isFirst; }BTnode;
void printPostorder(BinaryTree *pBinaryTree) { if (NULL == pBinaryTree) return;
stack<BTnode*> S; BinaryTree *pS = pBinaryTree; BTnode *pTemp; while ((pS != NULL) || !S.empty()) { while (pS) { BTnode *pBTnode = (BTnode*)malloc(sizeof(BTnode)); pBTnode->pTnode = pS; pBTnode->isFirst = true; S.push(pBTnode); pS = pS->left_child; } if (!S.empty()) { pTemp = S.top(); if (pTemp->isFirst == true) { pTemp->isFirst = false; pS = pTemp->pTnode->right_child; } else { cout << pTemp->pTnode->value << endl; S.pop(); } } }
} 方法二: 既然后序遍历需要添加一个变量,那么可以借助 STL 中的 pair 对组来进行绑定。pair 将两个值视作一个单元,我们可以设置第一个值为节点指针,第二个置为 bool 型变量,利用 make_pair() 函数来构建对组。在方法一中说到,后序遍历需保证节点的孩子均已访问完毕,再访问该节点。根据栈的特点,我们可以把遍历树的过程看做是一个将树节点压栈的过程,遍历顺序就等于压栈顺序以及何时出栈。将父节点二度入栈,第二次入栈修改标记,表明该节点的孩子节点访问完毕(压栈完毕)。 具体结合程序理解
void printPostorder(BinaryTree *pBinaryTree) { if (NULL == pBinaryTree) return;
stack<pair<BinaryTree*, bool>> S; BinaryTree *pS; bool isFirst; S.push(make_pair(pBinaryTree, true)); while (!S.empty()) { pS = S.top().first; isFirst = S.top().second; S.pop(); if (isFirst) { S.push(make_pair(pS, false)); if (pS->right_child) S.push(make_pair(pS->right_child, true)); if (pS->left_child) S.push(make_pair(pS->left_child, true)); } else { cout << pS->value << endl; } }
} 方法三:
后序遍历要保证父节点是最后被访问的,且最先被访问的是左孩子。比较棘手的地方是对于根(父)节点的访问,前面两种方法是采用标记的策略,我们也可以把访问根(父)节点的情况列出来,即在什么条件下可以对其进行访问了。以下面这可树为例:
1). 当该节点没有孩子节点的时候,可以直接访问,如节点1,节点10;2). 当该节点左右孩子节点,或只存在左孩子或右孩子等皆被访问完后,可以访问该节点;其余情况则可以按次序依次将右左孩子节点入栈,然后每次取栈顶元素,循环进行判断,直至栈为空。
先将节点6压栈,存在孩子节点,继而将节点8,2压栈(栈:6,8,2),取栈顶元素2,继续重复,将节点4,1压栈(栈:6,8,2,4,1),取栈顶1,为第一种情况,无孩子节点,则访问(出栈,栈:6,8,2,4),再取栈顶元素4,重复上述过程,没有左孩子或右孩子的情况当做已访问,或不理会。节点有无孩子节点容易判别,留下的问题就是怎么知道节点的孩子节点均已被访问,弹出一个节点之后,位于栈顶的总是父节点,这样也继续访问其左孩子,陷入死循环。我们发现按上述过程,如栈顶节点与刚出栈(表示已访问)的节点存在父子关系,则表明该栈顶节点的孩子节点均已访问完毕,可以访问该栈顶节点。这样我们就需要额外一个节点指针来指向刚出栈的节点,与栈顶节点进行 “亲子鉴定”。 具体参见程序:
void printPostorder(BinaryTree *pBinaryTree) { if (NULL == pBinaryTree) return;
stack<BinaryTree*> S; BinaryTree *pS; BinaryTree *pre = NULL; S.push(pBinaryTree); while (!S.empty()) { pS = S.top(); if ((NULL == pS->left_child) && (NULL == pS->right_child) || ((pre == pS->left_child) || (pre == pS->right_child))) { cout << pS->value << endl; S.pop(); pre = pS; } else { if (pS->right_child) S.push(pS->right_child); if (pS->left_child) S.push(pS->left_child); } }
}
|