鏈式二叉樹 先序、中序、後序 遍歷(遞迴、非遞迴)
阿新 • • 發佈:2018-11-27
參考部落格:click here!
鏈式二叉樹儲存結構:
typedef int DataType;
typedef struct BiNode {
DataType data;
struct BiNode *lc, *rc; // 左右子節點指標
int depth;
} BiNode, *BiTree;
初始化:
void rootInit(BiTree &root)
{
root = NULL;
assert(!root); // 檢查root是否合法
return;
}
先序遍歷: 根左右
遞迴先序遍歷遵循條件:
先訪問根節點, 然後訪問左子節點, 最後訪問右子節點。(根左右)
二叉樹為空則停止遞迴
void PreOrder(BiTree &root) // 遞迴先序遍歷
{
if(root == NULL)
return;
cout << root->data << " ";
PreOrder(root->lc);
PreOrder(root->rc);
}
非遞迴版本的先序遍歷:
1.若根節點為空(空樹,不需要遍歷),直接返回;
2.否則,將根節點入棧;
3.判斷棧是否為空,不為空進入迴圈;
4.取棧頂結點並訪問該節點,同時將該節點出棧;
5.若該節點的右子樹不為空,將右孩子結點入棧;
6.若該節點的左子樹不為空,將左孩子節點入棧;
7.直到棧為空,跳出迴圈,完成了先序遍歷。
void PreOrder2(BiTree &root) // 非遞迴先序遍歷 { stack<BiTree> s; // 定義一個棧 while( !s.empty()) s.pop(); // 清空 if(root != NULL) s.push(root); BiTree tmp; // 臨時 while( !s.empty()) // 棧不為空 { tmp = s.top(); // 獲得棧頂元素 s.pop(); cout << tmp->data << " "; if(tmp->lc != NULL) // 先序遍歷,必須先判斷右子樹 s.push(tmp->rc); if(tmp->rc != NULL) // 判斷右子樹 s.push(tmp->lc); } cout << endl; return; }
中序遍歷:左根右
遞迴中序遍歷遵循條件: 若二叉樹為空,直接返回;
1.遍歷二叉樹的左子樹
2.訪問根節點
3.遍歷二叉樹的右子樹
void MidOrder(BiTree &root) // 中序遍歷 遞迴
{
if(root == NULL)
return;
MidOrder(root->lc); // 先左
cout << root->data << " "; // 然後根
MidOrder(root->rc); // 最後右
}
非遞迴中序遍歷:
1.若樹為空,直接返回;否則令tmp=root,進入迴圈(跳出條件為棧為空 或 tmp==NULL)
2.迴圈的將tmp節點的所有左子樹入棧
3.取棧頂結點為tmp,訪問該節點並出棧
4.令tmp=tmp的右孩子,進入下一次迴圈
void MidOrder2(BiTree &root) // 中序遍歷 非遞迴
{
stack<BiTree> s;
while( !s.empty()) s.pop();
BiTree tmp = root;
while( !s.empty() || tmp != NULL)
{
while(tmp != NULL) // 中序遍歷,先向左邊找, 左邊沒有就輸出根
{
s.push(tmp);
tmp = tmp->lc;
}
tmp = s.top();
cout << tmp->data << " "; // 然後根
tmp = tmp->rc; // 向右
s.pop();
}
cout << endl;
}
後續遍歷:左右根
遞迴後序遍歷
void PostOrder(BiTree &root) // 後序遍歷
{
if(root == NULL)
return;
PostOrder(root->lc); // left
PostOrder(root->rc); // right
cout << root->data << " "; // root
}
非遞迴後序遍歷:
1.若樹為空,直接返回;否則令tmp= root,進入迴圈(條件為棧非空或tmp!=NULL)
2.迴圈的將所有左子樹入棧;
3.去棧頂結點,若該節點沒有右子樹或者右子樹已經訪問過了,則訪問該節點並出棧;
4.否則,令tmp為該節點的右子樹,進入下一層迴圈。
void PostOrder2(BiTree &root) // 非遞迴後序遍歷
{
stack<BiTree> s;
while( !s.empty()) s.pop();
BiTree tmp = root;
BiTree pre = NULL; // 記錄先前剛訪問過的結點
BiTree top; // 用於臨時儲存棧頂結點
while( !s.empty() || tmp != NULL)
{
while(tmp != NULL) // 先一直向左
{
s.push(tmp);
tmp = tmp->lc;
}
top = s.top();
if(top->rc == NULL || top->rc == pre) // 如果右子節點為空或者已經訪問過
{ // 如果沒有右子節點或者已經訪問過就輸出當前子樹的根節點,並出棧
cout << top->data << " ";
pre = top; // 用於從右子節點回溯的時候判斷右子節點是否已經訪問過
s.pop();
}
else
tmp = top->rc;
}
}
完整程式碼:
#include <iostream>
#include <assert.h>
#include <stack>
using namespace std;
typedef int DataType;
typedef struct BiNode {
DataType data;
struct BiNode *lc, *rc; // 左右子節點指標
int depth;
} BiNode, *BiTree;
void rootInit(BiTree &root)
{
root = NULL;
assert(!root); // 檢查root是否合法
return;
}
/* 先序遍歷 根左右 */
void PreOrder(BiTree &root) // 遞迴先序遍歷
{
if(root == NULL)
return;
cout << root->data << " ";
PreOrder(root->lc);
preOrder(root->rc);
}
void PreOrder2(BiTree &root) // 非遞迴先序遍歷
{
stack<BiTree> s; // 定義一個棧
while( !s.empty()) s.pop(); // 清空
if(root != NULL)
s.push(root);
BiTree tmp; // 臨時
while( !s.empty()) // 棧不為空
{
tmp = s.top(); // 獲得棧頂元素
s.pop();
cout << tmp->data << " ";
if(tmp->lc != NULL) // 先序遍歷,必須先判斷右子樹
s.push(tmp->rc);
if(tmp->rc != NULL) // 判斷右子樹
s.push(tmp->lc);
}
cout << endl;
return;
}
/* 中序遍歷 左根右 */
void MidOrder(BiTree &root) // 中序遍歷 遞迴
{
if(root == NULL)
return;
MidOrder(root->lc); // 先左
cout << root->data << " "; // 然後根
MidOrder(root->rc); // 最後右
}
void MidOrder2(BiTree &root) // 中序遍歷 非遞迴
{
stack<BiTree> s;
while( !s.empty()) s.pop();
BiTree tmp = root;
while( !s.empty() || tmp != NULL)
{
while(tmp != NULL) // 中序遍歷,先向左邊找, 左邊沒有就輸出根
{
s.push(tmp);
tmp = tmp->lc;
}
tmp = s.top();
cout << tmp->data << " "; // 然後根
tmp = tmp->rc; // 向右
s.pop();
}
cout << endl;
}
/* 後序遍歷 左右根 */
void PostOrder(BiTree &root) // 後序遍歷
{
if(root == NULL)
return;
PostOrder(root->lc); // left
PostOrder(root->rc); // right
cout << root->data << " "; // root
}
void PostOrder2(BiTree &root) // 非遞迴後序遍歷
{
stack<BiTree> s;
while( !s.empty()) s.pop();
BiTree tmp = root;
BiTree pre = NULL; // 記錄先前剛訪問過的結點
BiTree top; // 用於臨時儲存棧頂結點
while( !s.empty() || tmp != NULL)
{
while(tmp != NULL) // 先一直向左
{
s.push(tmp);
tmp = tmp->lc;
}
top = s.top();
if(top->rc == NULL || top->rc == pre) // 如果右子節點為空或者已經訪問過
{ // 如果沒有右子節點或者已經訪問過就輸出當前子樹的根節點,並出棧
cout << top->data << " ";
pre = top; // 用於從右子節點回溯的時候判斷右子節點是否已經訪問過
s.pop();
}
else
tmp = top->rc;
}
}
int main()
{
return 0;
}