本文章參考了:https://blog.csdn.net/zhangxiangdavaid/article/details/37115355 的總結;相對原文,力求更加簡要的對三種二叉樹遍歷的非遞歸算法進行歸納
一、二叉樹中序遍歷的非遞歸算法 - LNR
既然是非遞歸算法,我們自然要借助棧。那么關鍵就是確定什么時候進行入棧,訪問、出棧這幾個動作。
整個中序遞歸遍歷的思路理解起來並不難,他和我們手動用 LNR 寫出中序遍歷的思路很相近:
入棧:結點非空時,結點進棧,往左走;
訪問:棧非空,每出棧一個結點,便訪問並往右走;

當每次向左走到空葉結點時,有上圖兩種情況;但當我們使用空葉子結點時,左邊情況是右邊情況的一種,兩者可以統一處理,即:

所以中序遍歷的非遞歸代碼很簡潔:
//中序遍歷 void InOrderWithoutRecursion2(BTNode* root) { //空樹 if (root == NULL) return; //樹非空 BTNode* p = root; stack<BTNode*> s; while (!s.empty() || p) { if (p) { s.push(p); p = p->lchild; } else { p = s.top(); s.pop(); cout << setw(4) << p->data; //打印在出棧時 p = p->rchild; } }
二、二叉樹先序遍歷的非遞歸算法 - NLR
理解了一之后,再來看先序的非遞歸算法就很好理解了。兩者的區別,只是打印位置的提前,我們腦海中對一棵二叉樹的搜索過程是一樣的。直接給上代碼:
//前序遍歷 void PreOrderWithoutRecursion2(BTNode* root) { if (root == NULL) return; BTNode* p = root; stack<BTNode*> s; while (!s.empty() || p) { if (p) { cout << setw(4) << p->data;//打印在向左搜尋時 s.push(p); p = p->lchild; } else { p = s.top(); s.pop(); p = p->rchild; } } cout << endl;
三、二叉樹后續遍歷非遞歸算法 LRN
非遞歸后續遍歷算法是3者中最難的,但實際上還是一樣,重在理解:入棧,訪問、出棧的操作規律
關鍵是理解:訪問一個結點發生在,該節點無右孩子 或者 有右孩子但右孩子剛剛訪問
代碼的邏輯如下:

代碼:(來自https://www.cnblogs.com/Dawn-bin/p/9844442.html )
flag = 1表示是一路從左遍歷至空節點;
1 Status PostOrderTraverse(BiTree T){ 2 BiTree p = T, S[100], pre; 3 int top = 0, flag = 1; 4 if(p) 5 do{ 6 while(p){ 7 S[top++] = p; 8 p = p->lchild; 9 } 10 // p所有左節點入棧 11 flag = 1; 12 13 while(top != 0 && flag == 1){ 14 p = S[top-1]; 15 if(p->rchild == pre || p->rchild == NULL){ 16 //右孩子不存在或右孩子已訪問 17 top--; 18 printf("%c ", p->data); 19 pre = p; 20 //指向被訪問節點 21 } 22 else{ 23 //繼續遍歷右子樹 24 p = p->rchild; 25 flag = 0; 26 } 27 } 28 }while(top != 0); 29 return OK; 30 }//PostOrderTraverse
該算法的特點是,棧中所保存的是出棧結點至根的所有祖先結點,利用這點后續非遞歸遍歷有很多應該,比如:
(1).輸出某個葉子結點的所有祖先
(2).輸出根結點到所有葉子結點的路徑
(3).如果二叉樹結點的值是數值,那么求每條路徑上值之和
