在上一篇博客中我們已經簡單介紹過了二叉樹的創建和一些基本操作以及對於先序,中序,後序的遞歸遍歷,以及層序的非遞歸遍歷,二叉樹之創建與遞歸遍歷,如果對遞歸遍歷和層序的非遞歸有問題可以去看看;
本篇波可完整代碼 請看二叉樹
非遞歸遍歷
先序非遞歸
我們先來回顧一下在什麼是先序遍歷;
先序遍歷二叉樹:
若二叉樹爲空,則空操作;否則:
(1)訪問根結點;
(2)先序遍歷左子樹;
(3)先序遍歷右子樹;
這個在上次遞歸遍歷中已經說過了,所以我們就直接可以來進行非遞歸遍歷分析:
我們在考慮遞歸轉到非遞歸問題的時候,一定會用到一個數據結構,它就是棧,爲什麼是棧呢? 因爲在棧的特性就是後進先出和遞歸的思路差不多,所以我們用棧這個數據結構來代替遞歸,就把問題轉化到非遞歸上面來:
我們先用看代碼然後來解釋爲什麼這麼寫?
先序非遞歸代碼1:
void PreOrder_Nor()
{
cout << " PreOrder_Nor:" << endl;
stack<Node*> s;
Node* pCur = _pRoot;
s.push(pCur);
while (!s.empty())
{
Node* top = s.top();
cout << top->_data << " ";
s.pop();
if (top->_pRight)
s.push(top->_pRight);
if (top->_pLeft)
s.push(top->_pLeft);
}
}
因爲先序遍歷是根左右,棧是後進先出,所以在上面代碼中,先存儲了根結點,訪問完後就pop掉,然後存儲右結點在存儲左結點,棧頂先訪問的是左結點,就可以達到根左右的效果;
先序非遞歸代碼2:
void PreOrder_Nor()
{
stack<Node*> s;
Node* pCur = _pRoot;
while (!s.empty() || pCur)
{
while (pCur)
{
cout << pCur->_data << " ";
s.push(pCur);
pCur = pCur->_pLeft;
}
Node* top = s.top();
s.pop();
pCur = top->_pRight;
}
}
在上面的代碼中,我們可以看到是一上來就不管三七二十一先把他的左子樹的根結點全先訪問後入棧,然後在通過取棧頂元素來訪問他的右節點;
中序非遞歸
中序遍歷二叉樹:
若二叉樹爲空,則空操作;否則:
(1)中序遍歷左子樹;
(2)訪問根結點;
(3)中序遍歷右子樹;
和先序遍歷一樣,我們還是要利用棧這個數據結構來把遞歸轉爲非遞歸:
void InOrder_Nor()
{
cout << "InOrder_Nor:" << endl;
stack<Node*> s;
Node* pCur = _pRoot;
while (!s.empty() || pCur)
{
while (pCur)
{
s.push(pCur);
pCur = pCur->_pLeft;
}
Node* top = s.top();
s.pop();
cout << top->_data << " ";
pCur = top->_pRight;
}
}
在中序遍歷的時候是左根右來遍歷,所有我們在樹中先找到最左邊的葉子結點,從它開始訪問,所以上述的while循環中我們已經找到了最左邊的葉子結點,所以我們取棧頂元素就可以一步一步往回退着來訪問,其中最後一句中我們是不用管它的右子樹是否爲空,因爲在while(pCur)中可以判斷;
後序非遞歸
後序遍歷二叉樹:
若二叉樹爲空,則空操作;否則:
(1)後序遍歷左子樹;
(2)後序遍歷右子樹;
(3)訪問根結點;
void PostOrder_Nor()
{
cout << "PostOrder_Node:" << endl;
stack<Node*> s;
Node* pCur = _pRoot;
Node* prve = NULL; // 用來存放當前節點的前一個節點
while (!s.empty() || pCur)
{
while (pCur)
{
s.push(pCur);
pCur = pCur->_pLeft;
}
Node* top = s.top();
if (NULL == top->_pRight || top->_pRight == prve) //沒有後面這個條件可能造成對一個有右節點的樹死循環訪問它的右子樹
{
cout << top->_data << " ";
prve = top; //對它的前一個節點進行更新
s.pop();
}
else
pCur = top->_pRight;
}
}
咋後序遍歷中,我們是左右根來遍歷的,所以我們還是利用棧來存儲結點,其中與其他不一樣的是,在後序遍歷中我們要yoga一個指針來保存上一個結點,不然的還在註釋那一行如果樹的右子樹存在就會死循環,所以只要注意這一點,後序遍歷並不難;