二叉樹先序遍歷的非遞歸算法具體實(shí)現(xiàn)
在前面一文,說過二叉樹的遞歸遍歷算法(二叉樹先根(先序)遍歷的改進(jìn)),此文主要講二叉樹的非遞歸算法,采用棧結(jié)構(gòu)
總結(jié)先根遍歷得到的非遞歸算法思想如下:
1)入棧,主要是先頭結(jié)點(diǎn)入棧,然后visit此結(jié)點(diǎn)
2)while,循環(huán)遍歷當(dāng)前結(jié)點(diǎn),直至左孩子沒有結(jié)點(diǎn)
3)if結(jié)點(diǎn)的右孩子為真,轉(zhuǎn)入1)繼續(xù)遍歷,否則退出當(dāng)前結(jié)點(diǎn)轉(zhuǎn)入父母結(jié)點(diǎn)遍歷轉(zhuǎn)入1)
先看符合此思想的算法:
int PreOrderTraverseNonRecursiveEx(const BiTree &T, int (*VisitNode)(TElemType data))
{
if (T == NULL)
{
return -1;
}
BiTNode *pBiNode = T;
SqStack S;
InitStack(&S);
Push(&S, (SElemType)T);
while (!IsStackEmpty(S))
{
while (pBiNode)
{
VisitNode(pBiNode->data);
if (pBiNode != T)
{
Push(&S, (SElemType)pBiNode);
}
pBiNode = pBiNode->lchild;
}
if(pBiNode == NULL)
{
Pop(&S, (SElemType*)&pBiNode);
}
if ( pBiNode->rchild == NULL)
{
Pop(&S, (SElemType*)&pBiNode); //如果此時(shí)棧已空,就有問題
}
pBiNode = pBiNode->rchild;
}
return 0;
}
注意:1)這里使用了棧結(jié)構(gòu),可參看上文順序結(jié)構(gòu)存儲(chǔ)的棧
2)這里在保存結(jié)點(diǎn)的時(shí)候,我保存的是指針也就是結(jié)點(diǎn)的地址,將其變?yōu)閕nt型存儲(chǔ),在pop的時(shí)候里面使用的是指針,所以取的是&pBiNode,而不是pBiNode,為什么請(qǐng)自行思考指針的使用,最好理解的就是BiTNode *pBiNode;定義改為BiTree pBiNode就很好理解了。
上面這個(gè)算法其實(shí)是錯(cuò)誤的!為什么呢? 這里我檢查好久,期間出現(xiàn)還出現(xiàn)過無限循環(huán),也出現(xiàn)過從左子樹退出后右邊子樹不顯示,最后我修改了第一個(gè)while判斷條件,為什么呢?因?yàn)槿绻趐op之后,棧已空但是右子樹還有,就無法繼續(xù)了,這個(gè)在我寫出后并沒有進(jìn)行太多驗(yàn)證,后面再闡述,這里并沒有壓入null指針,看一下壓入空指針的例子,主要是左子樹為空的時(shí)候才壓入棧的,如下:
int PreOrderTraverseNonRecursive(const BiTree &T, int (*VisitNode)(TElemType data))
{
if (T == NULL)
{
return -1;
}
BiTNode *pBiNode = T;
SqStack S;
InitStack(&S);
Push(&S, (SElemType)T);
while (!IsStackEmpty(S))
{
GetTop(S, (SElemType*)&pBiNode);
while (pBiNode)
{
VisitNode(pBiNode->data);
pBiNode = pBiNode->lchild;
Push(&S, (SElemType)pBiNode);
}
if(pBiNode == NULL)
{
Pop(&S, (SElemType*)&pBiNode);
}
if ( !IsStackEmpty(S))
{
Pop(&S, (SElemType*)&pBiNode);
pBiNode = pBiNode->rchild;
Push(&S, (SElemType)pBiNode);
}
}
return 0;
}
這里是這樣的,先壓入根節(jié)點(diǎn),然后判斷左子樹是否為空,不為空就壓入棧,否則退出while循環(huán)之后就將NULL結(jié)點(diǎn)出棧,再判斷當(dāng)前棧是否為空,如果非空就出棧得到父節(jié)點(diǎn)然后判斷右孩子,壓入右孩子結(jié)點(diǎn),再判斷此右子樹的左孩子是否為空,繼續(xù)循環(huán)。
這里有兩個(gè)浪費(fèi)的地方:一個(gè)就是壓入空孩子結(jié)點(diǎn)入棧,二就是頻繁使用GetTop獲得棧頂元素
這里返回過來再看初開始設(shè)計(jì)的算法,那里正好沒有壓入NULL指針或者說空的孩子結(jié)點(diǎn),但是并不能輸出完整,這里我們想到可以在判斷棧的時(shí)候加入,當(dāng)前的結(jié)點(diǎn)是否為NULL就可以了,這樣就不會(huì)出現(xiàn)不會(huì)顯示退出左子樹結(jié)點(diǎn)不能顯示右子樹結(jié)點(diǎn)的尷尬了,如下:
//非遞歸先序遍歷二叉樹
int PreOrderTraverseNonRecursiveEx(const BiTree &T,
int (*VisitNode)(TElemType data))
{
if (T == NULL)
{
return -1;
}
BiTNode *pBiNode = T;
SqStack S;
InitStack(&S);
Push(&S, (SElemType)T);
while ( !IsStackEmpty(S) || pBiNode) //主要修改的就是這句
{
while (pBiNode)
{
VisitNode(pBiNode->data);
if (pBiNode != T)
{
Push(&S, (SElemType)pBiNode);
}
pBiNode = pBiNode->lchild;
}
if(pBiNode == NULL)
{
Pop(&S, (SElemType*)&pBiNode);
}
if ( pBiNode->rchild == NULL)
{
Pop(&S, (SElemType*)&pBiNode); //如果此時(shí)棧已空,就有問題
}
pBiNode = pBiNode->rchild;
}
return 0;
}
在第一個(gè)while循環(huán)加入這個(gè)之后,就可以了,測(cè)試用例與二叉樹先序遍歷類似。如下測(cè)試上節(jié)的二叉樹例子:
此時(shí)輸入的數(shù)據(jù)仍然還是 12 34 0 0 78 0 0,測(cè)試結(jié)果如下:
--- BiTree ---
Please Enter BiTree Node data:
12
Please Enter BiTree Node data:
34
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
78
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
12 34 78
這個(gè)還不足以測(cè)試,再看如下的二叉樹
此時(shí)輸入數(shù)據(jù)應(yīng)該為:12 34 24 0 0 50 0 0 78 37 0 0 0,測(cè)試結(jié)果如下:
--- BiTree ---
Please Enter BiTree Node data:
12
Please Enter BiTree Node data:
34
Please Enter BiTree Node data:
24
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
50
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
78
Please Enter BiTree Node data:
37
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
Please Enter BiTree Node data:
0
12 34 24 50 78 37
由先序遍歷可知,正好是正確的,另外這些算法不光是對(duì)先序遍歷的,如果想變?yōu)橹行蚧蛘吆笮?,只需將上面算法中的visit之類的先去掉,然后將它加入合適的位置,就可以了
相關(guān)文章
JavaScript判斷一個(gè)URL鏈接是否有效的實(shí)現(xiàn)方法
如何用javascript來判斷請(qǐng)求的url/鏈接有效(可連接,可用)?需要的朋友可以參考下。2011-10-10原生js實(shí)現(xiàn)隨機(jī)點(diǎn)名功能
這篇文章主要為大家詳細(xì)介紹了原生js實(shí)現(xiàn)隨機(jī)點(diǎn)名功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11JavaScript數(shù)據(jù)類型轉(zhuǎn)換詳解(推薦)
眾所周知JavaScript是一門弱類型(語言,即變量的類型是不確定的。所以下面這篇文章主要給大家介紹了關(guān)于JavaScript數(shù)據(jù)類型轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2021-05-05JAVASCRIPT IE 與 FF中兼容問題小結(jié)
在不同瀏覽器中對(duì)于一些屬性的支持也不一樣,下面是對(duì)ie和firefox的一些小結(jié)。2009-02-02JavaScript利用時(shí)間分片實(shí)現(xiàn)高性能渲染數(shù)據(jù)詳解
為了豐富我們的知識(shí)體系,我們有必要了解并清楚當(dāng)遇到大量數(shù)據(jù)時(shí),如何才能在不卡主頁面的情況下渲染數(shù)據(jù),以及其中背后的原理,本文介紹了如何使用時(shí)間分片的方式來渲染大量數(shù)據(jù),感興趣的可以了解下2023-05-05Bootstrap Table快速完美搭建后臺(tái)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Bootstrap Table快速完美搭建后臺(tái)管理系統(tǒng)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09js實(shí)現(xiàn)上傳圖片及時(shí)預(yù)覽
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)上傳圖片及時(shí)預(yù)覽的相關(guān)資料,具有一定的參考價(jià)值,感興趣的朋友可以參考一下2016-05-05