本篇文章主要詳解利用棧的方式二叉樹先序、中序、后序遍歷的非遞歸寫法
首先我們需要實現一顆二叉樹。
以下是通過先序序列建樹的代碼
例如:先序序列{1,2,4,10,'#','#',6,11,'#','#',7'#','#','#',3,8,'#','#',9}代表以下的樹
typedef struct Node
{
int _data;
Node* _right;
Node* _left;
Node()
{}
Node(int x)
{
_data = x;
_left = NULL;
_right = NULL;
}
}*pNode;
//參數類型arr 數組指針,index 下標,size 數組元素個數,invalid代表先序遍歷時遇到的NULL
pNode createTree_PrevOrder( const int* arr, size_t& index,const size_t size,int invalid){Node* root = NULL;if (index < size && arr[index] != invalid){root = new Node(arr[index]);root->_left = createTree_PrevOrder(arr, ++index, size, invalid);root->_right = createTree_PrevOrder(arr, ++index, size, invalid);}return root;}
先序非遞歸:
先序遍歷時,每當我們壓入一個結點,我們壓入結點前對其進行訪問。
void prevOrder_NR(pNode root)
{
if (NULL == root)
cout << "empty!" << endl;
stack<pNode> s;
pNode cur = root;
while (cur || !s.empty())
{
while (cur) //判斷:cur為空,遍歷棧頂結點的右子樹結點,cur不為空;訪問cur,並遍歷cur的左子樹結點
{
cout << cur->_data << "->";//遍歷一個結點則進行訪問
s.push(cur);
cur = cur->_left;
}
cur = s.top(); //此時cur為空,因此需要對cur進行處理
s.pop(); //將棧頂結點的右子樹結點壓入棧前,將棧頂結點彈出,避免死循環
cur = cur->_right;//在下一個while時將右子樹結點壓入棧
}
cout << "over"<<endl;
}
中序非遞歸:
中序時我們需要在遍歷完左子樹后訪問根節點,再去遍歷右子樹,因此我們僅需依照先序遍歷修改部分代碼。
void inOrder_NR(pNode root)
{
if (NULL == root)
cout << "empty" << endl;
stack<pNode> s;
pNode cur = root;
while (cur || !s.empty())
{
while (cur)//壓入左子樹結點
{
s.push(cur);
cur = cur->_left;
}
cur = s.top();//取棧頂結點
cout << cur->_data << "->" ;//訪問左結點,此時棧頂節點有兩種情況:1.左子樹結點為空;2.左子樹結點已被訪問彈出
s.pop(); //彈出訪問過的結點
cur = cur->_right; //將棧頂結點的右子樹結點入棧
}
cout << "over" << endl;
}
后序遍歷:
后序遍歷時由於訪問完左右子樹后才能訪問根結點,因此需要將根結點在棧內保留到左右子樹被訪問后,但同時會出現一個問題,當右子樹彈出后遇到根結點又會將右子樹結點壓入棧中,造成死循環,因此我們需要在定義一個變量last代表最后一個訪問的結點,當last與棧頂結點的右子樹結點相同時,則不再將右子樹結點壓入棧中。
void pastOrder_NR(pNode root)
{
if (NULL == root)
cout << "empty" << endl;
pNode cur = root;
pNode last = NULL;
stack<pNode> s;
while (cur || !s.empty())
{
while (cur)//壓入左子樹結點
{
s.push(cur);
cur = cur->_left;
}
cur = s.top();
if (cur->_right && last != cur->_right)//考慮棧頂結點的右子樹結點。存在且沒被訪問過,將右子樹結點壓入棧中
{
cur = cur->_right;
}
else if ((NULL == cur->_right) || (last == cur->_right))
//右子樹結點為空或者已經被訪問過,則訪問棧頂結點並彈出
{
cout << cur->_data << "->";
last = cur;//更新last值
s.pop();
//cur置空作用在於當原棧頂結點被訪問並彈出后,下一層while是將當前棧頂結點的左子樹入棧,當前棧頂結點的左子樹已經被遍歷過,
//因此會造成死循環,所以將cur置空,直接考慮當前棧頂結點的右子樹
//一旦某個結點入棧,首先會遍歷這個結點的左子樹,然后考慮右子樹的情況
cur = NULL;
}
}cout << "over" << endl;
}
附帶遞歸寫法用於測試正確性
void prevOrder(pNode root)
{
if (root == NULL)
return;
cout << root->_data << "->";
prevOrder(root->_left);
prevOrder(root->_right);
}
void pastOrder(pNode root)
{
if (root == NULL) return;
pastOrder(root->_left);
pastOrder(root->_right);
cout << root->_data << "->";
}
void inOrder(pNode root)
{
if (root == NULL) return;
inOrder(root->_left);
cout << root->_data << "->";
inOrder(root->_right);
}
void test()
{
int arr[] = { 1, 2, 4, 10, '#', '#', 6, 11, '#', '#', 7,'#', '#', '#', 3, 8, '#', '#', 9 };
size_t index = 0;
pNode root=createTree_PrevOrder(arr, index, sizeof(arr)/sizeof(arr[0]), '#');
cout << "prevOrder_NR:" ;
prevOrder_NR(root);
cout << "prevOrder :";
prevOrder(root);
cout << endl;
cout << "inOrder_NR:";
inOrder_NR(root);
cout << "inOrder :";
inOrder(root);
cout << endl;
cout << "pastOrder_NR:";
pastOrder_NR(root);
cout << "pastOrder :";
pastOrder(root);
cout << endl;
}
測試結果:
---------------------
版權聲明:本文為CSDN博主「Mr_Wayne__」的原創文章,遵循CC 4.0 by-sa版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_38134165/article/details/79871430
