一、什麼是樹?什麼又是二叉樹
(1)樹的定義
樹是一種特殊的數據結構,是由N(N>=0)個節點構成的具有層次關係的集合。每棵樹都有一個特殊的結點,就是根結點,根節點沒有前驅結點;除過根節點的其餘結點被分爲若干個互不相交的集合,稱之爲子樹;每個子樹的根結點有且僅有一個前驅,可以有0個或者多個後繼
(2)那麼問題來了,什麼是二叉樹嘞?
二叉樹是每個結點最多有兩個子樹的有序樹(它由一個根節點加上它的左右子樹組合起來的)。
* 特點:(1)每個結點最多有兩棵子樹,即二叉樹不存在度大於2的結點(分支數最大不超過2)
(2)二叉樹的子樹有左右之分,其子樹的次序不能被顛倒
* 二叉樹有五種基本形態:(如下圖所示)
(1)空二叉樹
(2)只有一個根節點
(3)只有左子樹
(4)只有右子樹
(5)完全二叉樹
- 滿二叉樹:一棵二叉樹上的所有分支結點都存在左右子樹,並且葉子結點都在同一層上面
- 完全二叉樹:如果一個具有N個結點的二叉樹與之滿二叉樹的前N個結點的結構相同,就是完全二叉樹
二叉樹的四種遍歷
1、前序遍歷(VLR):根節點—>左子樹—>右子樹
【遞歸遍歷】
算法如下:
判斷二叉樹是否爲空,倘若爲空,則return;否則:
* (1)先訪問根節點
* (2)然後前序遍歷根節點的左子樹
* (3)最後前序遍歷根節點的右子樹
代碼實現:
//前序遞歸遍歷
void PreOrder()
{
cout<<"前序遍歷"<<endl;
_PreOrder(_pRoot);
cout<<endl;
}
//前序遞歸: 根---根的左子樹---根的右子樹
void _PreOrder(PNode& pRoot)
{
if(pRoot)
{
cout<< pRoot->_data << "";
_PreOrder(pRoot->_pLeft);
_PreOrder(pRoot->_pRight);
}
}
【非遞歸遍歷】:(藉助棧實現)
算法如下:
判斷二叉樹是否爲空,倘若爲空,則return;否則:
(1)初始化一個棧
(2)根節點入棧
(3)當棧不爲空時,循環執行下列步驟
* 訪問取出棧頂元素
* 倘若該被訪問的節點的左子樹非空時,將該結點左子樹指針入棧
* 倘若該被訪問的節點的右子樹非空時,將該結點右子樹指針入棧
(4)重複執行直至棧爲空,return
代碼實現:
//前序非遞歸
void PreOrder_N()
{
_PreOrder_N(_pRoot);
}
//前序非遞歸
void _PreOrder_N(PNode& pRoot)
{
//判斷樹是否存在
if(NULL == pRoot)
return;
stack<PNode> s;
s.push(pRoot);
while(!s.empty())
{
PNode pTop = s.top();
cout<<pTop->_data<<"";
if(pTop->_pLeft)
s.push(pTop->_pLeft);
if(pTop->_pRight)
s.push(pTop->_pRight);
}
cout<<endl;
}
2、中序遍歷(LVR):左子樹—>根節點—>右子樹
【遞歸遍歷】
算法如下:
判斷二叉樹是否爲空,倘若爲空,則return;否則:
* (1)先中序遍歷根節點的左子樹
* (2)訪問根節點
* (3)最後中序遍歷根節點的右子樹
代碼實現:
//中序遞歸遍歷
void InOrder()
{
cout<<"中序遍歷"<<endl;
_InOrder(_pRoot);
cout<<" "<<endl;
}
//中序遞歸: 左子樹---根節點---右子樹
void _InOrder(PNode pRoot)
{
if(pRoot)
{
_InOrder(pRoot->_pLeft);
cout<< pRoot->_data <<"";
_InOrder(pRoot->_pRight);
}
}
【非遞歸遍歷】
算法如下:
判斷二叉樹是否爲空,倘若爲空,則return;否則:
(1)初始化一個棧
(2)給定一個任意節點pCur,倘若pCur的左孩子不爲空時,則將其入棧,並將pCur指向pCur的左孩子;(一直循環執行,直至左孩子爲空)
(3)執行完步驟(2)後,說明最左邊的結點已被找到,訪問輸出棧頂元素,並進行出棧操作
(4)將pCur指向其右孩子
(5)重複2、3、4步驟,直至當前結點爲空或者棧爲空,return
代碼實現:
//中序非遞歸
void InOrder_N()
{
_InOrder_N(_pRoot);
}
//中序非遞歸
void _InOrder_N(PNode pRoot)
{
if(NULL == pRoot)
return;
stack<PNode> s;
PNode pCur = pRoot;
while(pCur || !s.empty())
{
while(pCur)
{
s.push(pCur);
pCur = pCur->_pLeft;
}
pCur = s.top();
cout<<pCur->_data<<"";
s.pop();
pCur = pCur->_pRight;
}
}
3、後序遍歷(LRV):左子樹—>右子樹—>根節點
【遞歸遍歷】
算法如下:
判斷二叉樹是否爲空,倘若爲空,則return;否則:
* (1)先後序遍歷根節點的左子樹
* (2)後序遍歷根節點的右子樹
* (3)訪問根節點
代碼實現:
//後序遞歸遍歷
void PostOrder()
{
cout<<"後序遍歷"<<endl;
_PostOrder(_pRoot);
cout<<""<<endl;
}
//後序遞歸: 左子樹---右子樹---根節點
void _PostOrder(PNode pRoot)
{
if(pRoot)
{
_PostOrder(pRoot->_pLeft);
_PostOrder(pRoot->_pRight);
cout<< pRoot->_data <<"";
}
}
【非遞歸遍歷】:出棧順序爲左右根,則壓棧順序爲根右左
算法如下:
判斷二叉樹是否爲空,倘若爲空,則return;否則:
(1)初始化一個棧
(2)給定一個任意節點pCur
(3)將PCur入棧,倘若pCur的左孩子不爲空時,將pCur指向pCur的左孩子;(一直循環執行,直至左孩子爲空)
(4)執行完步驟(2)後,說明最左邊的結點已被找到,訪問棧頂元素
(5)此時倘若棧頂元素沒有右孩子,或者有右孩子但已被訪問輸出;則直接打印輸出該結點,並將其出棧
(6)否則當不滿足(4)的情況時,將該結點的右孩子給pCur
(7)重複3、4、5、6步驟,直至棧爲空,return
代碼實現:
//後序非遞歸
void PostOrder_N()
{
_PostOrder_N(_pRoot);
}
//後序非遞歸
void _PostOrder_N(PNode pRoot)
{
if(NULL == pRoot)
return;
stack<PNode> s;
PNode pCur = pRoot;
PNode prev = NULL:
while()
{
while()
{
s.push(pCur);
pCur = pCur->_pLeft;
}
pTop = s.top();
if(NULL == pTop->_pRight || prev == pTop->_pRight)
{
cout<<pTop->_data<<"";
s.pop;
}
else
{
pCur = pTop->_pRight;
}
}
}
4、層序遍歷(非遞歸)
層序遍歷就是:從根節點到葉子結點按照同一層先左子樹後右子樹的次序遍歷二叉樹。層序遍歷用隊列的方式實現
算法如下:
(1)初始化一個隊列
(2)將根節點的指針入隊列,即插入隊尾
(3)倘若隊列非空,則循環執行下列步驟:
* 從對頭取出一個結點訪問
* 倘若被訪問的結點非空,則讓其左子樹指針入隊列
* 倘若被訪問的結點非空,則讓其左子樹指針入隊列
(4)隊列清空後,return
代碼實現:
//層序遍歷
void LevelOrder()
{
cout<<"層序遍歷"<<endl;
_LevelOrder(_pRoot);
cout<<""<<endl;
}
//層序遍歷
void _LevelOrder(PNode pRoot)
{
if(pRoot)
{
queue<PNode> q;
q.push(pRoot);
while(!q.empty())
{
PNode pCur = q.front();
cout<< pCur->_data <<"";
q.pop();
if(pCur->_pLeft)
q.push(pCur->_pLeft);
if(pCur->_pRight)
q.push(pCur->_pRight);
}
}
}