資料結構| |二叉樹的三種遍歷方式(遞迴&&非遞迴)
阿新 • • 發佈:2019-01-22
首先來寫一下遞迴的!
對於遞迴要將大問題轉化為小問題,並且要有一個結束的位置。
比如:要前序遍歷一個二叉樹,那就是先訪問根節點,然後在訪問根節點的左子樹,在訪問根節點的右子樹,而左子樹與右子樹,又可以變成訪問該節點和該結點的左子樹和右子樹。這就變成了一個遞迴的思想了。而終止條件就是:直到訪問到葉子節點。
//遞迴
//前序遍歷二叉樹(根節點 左子樹 右子樹)
void BinaryTreePrevOrder(BTNode* root)
{
//終止條件
if (root == NULL)
{
return;
}
//先訪問根節點,然後遞迴訪問根節點的左子樹和右子樹
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
//中序遍歷二叉樹(左子樹 根節點 右子樹)
void BinaryTreeInOrder(BTNode* root)
{
//終止條件
if (root == NULL)
{
return;
}
//先遞迴訪問左子樹,在訪問根節點,在遞迴訪問右子樹
BinaryTreeInOrder(root-> _left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
//後序遍歷二叉樹(左子樹 右子樹 根節點)
void BinaryTreePostOrder(BTNode* root)
{
//終止條件
if (root == NULL)
{
return;
}
//先遞迴訪問左子樹。在遞迴訪問右子樹,最後訪問根節點
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root-> _right);
printf("%c ", root->_data);
}
接下來再寫一下非遞迴的。(使用棧)
對於非遞迴的二叉樹的遍歷。
//非遞迴遍歷二叉樹
//前序遍歷(根節點 左子樹 右子樹)
//思路:先將該二叉樹的左路結點壓棧遍歷完,然後在訪問根節點,然後再對右結點進行剛才同樣的事情,
//進行左路結點壓棧遍歷,然後在訪問右結點,結束條件:該節點是葉子就不需要在進行壓棧,對其進行出棧就可以了
void BinaryTreePrevOrderNonR(BTNode* root)
{
assert(root);
Stack s;
StackInit(&s);
BTNode* cur = root;
//StackEmpty必須存在因為在遍歷的過程中,cur有可能會等於NULL
while (cur || StackEmpty(&s))
{
//將左路結點進行壓棧
while (cur)
{
StackPush(&s, cur);
printf("%c ", cur->_data);
cur = cur->_left;
}
BTNode* top = StackTop(&s);
StackPop(&s);
cur = top->_right;
}
printf("\n");
}
//中序遍歷(左子樹 根節點 右子樹)
//思想:中序遍歷和前序遍歷基本一樣,只是在輸出結點資料的位置不同,中序遍歷是在對該節點進行出棧的時候,訪問該節點
void BinaryTreeInOrderNonR(BTNode* root)
{
assert(root);
Stack s;
StackInit(&s);
BTNode* cur = root;
//StackEmpty必須存在因為在遍歷的過程中,cur有可能會等於NULL
while (cur || StackEmpty(&s))
{
//將左路結點進行壓棧
while (cur)
{
StackPush(&s, cur);
cur = cur->_left;
}
BTNode* top = StackTop(&s);
printf("%c ", top->_data);
StackPop(&s);
cur = top->_right;
}
printf("\n");
}
//後序遍歷(左子樹 右子樹 根節點)
//思想:先將左路結點全部入棧,然後出棧的時候
//如何判斷是否要訪問該結點,
//如果該節點的右結點為空的話,或者該結點的右結點等於剛訪問的上一個節點prev,那就直接訪問該節點
//否則就是該節點的右結點沒有進行訪問呢,那就訪問該節點的右結點
void BinaryTreePostOrderNonR(BTNode* root)
{
assert(root);
Stack s;
StackInit(&s);
//prev記錄的是出棧的結點
BTNode* prev = NULL;
BTNode* cur = root;
//cur必須存在因為開始要從cur進入,只不過是在遍歷的時候,cur絕對不會等於NULL
while (cur || StackEmpty(&s))
{
//將左路結點全部入棧
while (cur)
{
StackPush(&s, cur);
cur = cur->_left;
}
BTNode* top = StackTop(&s);
//說明該節點的右結點已經訪問過了,可以進行輸出了
if (top->_right == NULL || top->_right == prev)
{
StackPop(&s);
prev = top;
printf("%c ", top->_data);
}
//該節點的右結點沒有訪問過,那就繼續對右結點進行子迴圈
else
{
cur = top->_right;
}
}
printf("\n");
}