之前,我們寫過二叉樹的一些基本操作,大部分是通過遞歸來實現的。想想看,要是不採用遞歸的方式,我們該如何對二叉樹進行操作?
下面,我們用非遞歸方式完成二叉樹的先序、中序和後序遍歷。
1.通過循環實現樹的先序遍歷
還是我們的那顆二叉樹:
我們先想一下,先序遍歷的順序是:根結點 —> 左子樹 —> 右子樹。只要我們先打印根結點,然後再打印根結點的左右子樹,每一個節點都這樣循環的進行,直到遍歷完所有的結點。
思路:這裏,我們建一個棧,先將根結點入棧,對照上圖,即 A 入棧,循環的取棧頂元素,出棧,並且訪問棧頂元素,即第一次打印 A ,然後先將該元素的右子樹入棧 即C,再將左子樹 B 入棧,重複上述步驟,直到棧爲空,說明遍歷結束。
代碼如下:
void TreePreOrderByLoop(TreeNode* root)
{
if(root == NULL)
{
return;
}
//1.先把根節點入棧
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,root);
//2.循環的取棧頂元素,棧爲空,說明循環結束
TreeNode* cur;
while(1)
{
int ret = SeqStackTop(&stack,&cur);
if(ret == 0){
break;
}
//a)取棧頂元素爲當前元素;
//b)出棧
SeqStackPop(&stack);
//c)訪問當前元素
printf("%c ",cur->data);
//d)將當前點的右子樹入棧
if(cur->rchild != NULL)
{
SeqStackPush(&stack,cur->rchild);
}
//e)將當前點的左子樹入棧
if(cur->lchild != NULL)
{
SeqStackPush(&stack,cur->lchild);
}
}
printf("\n");
return;
}
2.通過循環實現樹的中序遍歷
思路:定義一個cur指針,先將cur 指向root,循環判斷cur是否爲空
如果不爲空,入棧,然後將cur指向cur的 lchild;
如果爲空,取棧頂元素,出棧並且訪問它,再將cur指向棧頂元素的 rchild。直到棧爲空,遍歷結束。
具體實現代碼如下:
void TreeInOrderByLoop(TreeNode* root)
{
if(root == NULL)
{
return;//非法輸入
}
SeqStack stack;
SeqStackInit(&stack);
//1.先將cur指向root
TreeNode* cur = root;
//2.循環的判斷cur是否爲空,
while(1){
while(cur != NULL)
{
// 如果不爲空,入棧,然後讓cur指向cur的lchild
SeqStackPush(&stack,cur);
cur = cur->lchild;
}
// 如果爲空,取棧頂元素,訪問出棧,cur指向cur的rchild
TreeNode* top = NULL;
int ret = SeqStackTop(&stack,&top);
if(ret == 0)
{
return;//棧爲空,遍歷完成
}
printf("%c ",top->data);
SeqStackPop(&stack);
cur = top->rchild;
}
}
3.通過循環實現樹的後序遍歷
思路:後序遍歷和中序遍歷思路大體一致,但有一個很重要的區別:當取到棧頂元素後我們不能立即訪問它,因爲不確定它的右子樹是否已經訪問過。(後序遍歷的順序:左子樹 —> 右子樹 —>根節點)。因此,訪問棧頂元素需要滿足兩個條件:1.當前節點右子樹爲空;2.右子樹已經訪問過了(即當前節點的上一個節點,後序遍歷根節點前一個就是右子樹)。
代碼如下:
void TreePostOrderByLoop(TreeNode* root)
{
if(root == NULL)
{
return;//非法輸入
}
SeqStack stack;
SeqStackInit(&stack);
//1.先讓cur指向root
TreeNode* cur = root;
//pre保存上一個訪問過的元素
TreeNode* pre = NULL;
//2.循環的判斷cur是否爲空
while(1){
while(cur != NULL)
{
//3.如果不爲空,壓棧
SeqStackPush(&stack,cur);
//讓cur指向cur的左孩子結點
pre = cur;
cur = cur->lchild;
}
//4.如果爲空,循環取棧頂元素,對棧頂元素進行判定,
TreeNode* top;
int ret = SeqStackTop(&stack,&top);
if(ret == 0)
{
//棧爲空,說明遍歷完成
return;
}
// a)如果棧頂元素的右子樹訪問過了,和訪問的上一個元素是同一個
// b)或者棧頂元素沒有右子樹
// 就訪問棧頂元素,然後出棧
if(top->rchild == NULL || top->rchild == pre)
{
printf("%c ",top->data);
SeqStackPop(&stack);
pre = top;
}
else
{
//5.如果不滿足以上條件,就讓cur指向棧頂元素的右子樹,重複循環
cur = top->rchild;
}
}
return;
}