C 語言二叉樹幾種遍歷方法詳解及實(shí)例
二叉樹的一些概念
二叉樹就是每個(gè)結(jié)點(diǎn)最多有兩個(gè)子樹的樹形存儲(chǔ)結(jié)構(gòu)。先上圖,方便后面分析。

1 滿二叉樹和完全二叉樹
上圖就是典型的二叉樹,其中左邊的圖還叫做滿二叉樹,右邊是完全二叉樹。然后我們可以得出結(jié)論,滿二叉樹一定是完全二叉樹,但是反過來就不一定。滿二叉樹的定義是除了葉子結(jié)點(diǎn),其它結(jié)點(diǎn)左右孩子都有,深度為k的滿二叉樹,結(jié)點(diǎn)數(shù)就是2的k次方減1。完全二叉樹是每個(gè)結(jié)點(diǎn)都與深度為k的滿二叉樹中編號(hào)從1到n一一對(duì)應(yīng)。
2 樹的深度
樹的最大層次就是深度,比如上圖,深度是4。很容易得出,深度為k的樹,擁有的最大結(jié)點(diǎn)數(shù)是2的k次方減1。
3 樹的孩子,兄弟,雙親
上圖中,B,C是A的孩子,B,C之間互為兄弟,A是B,C的雙親。
二如何創(chuàng)建二叉樹
先說說二叉樹的存儲(chǔ)結(jié)構(gòu),跟很多其它模型一樣,也有順序和鏈?zhǔn)絻煞N方式。前者雖然使用簡單,但是存在浪費(fèi)空間的問題,舉個(gè)例子,下圖的二叉樹,用順序的方式存儲(chǔ)(0表示空,沒有子樹)是:
1 2 3 4 5 6 7 0 0 0 0 8 0 0 0

是不是相當(dāng)浪費(fèi)空間呢。
鏈?zhǔn)浇Y(jié)構(gòu)可以定義如下:
typedef struct _BiTNode
{
int data;
_BiTNode *leftChild;
_BiTNode *rightChild;
}BiTNode, *pBiTree;
然后就可以寫一個(gè)函數(shù)來創(chuàng)建二叉樹,過程是在控制臺(tái)輸入a表示退出當(dāng)前這一層,不再為該層創(chuàng)建左右孩子。輸入其它字母表示繼續(xù)創(chuàng)建。比如下面的輸入序列:

創(chuàng)建了如下結(jié)構(gòu)的二叉樹,

每個(gè)結(jié)點(diǎn)里的數(shù)值是隨機(jī)生成的小于100的數(shù)字。同時(shí)我也寫了一個(gè)自動(dòng)的命令序列函數(shù),方便測(cè)試,不用手動(dòng)輸入,非自動(dòng)和自動(dòng)創(chuàng)建的函數(shù)如下:
//創(chuàng)建二叉樹, 先序順序
int CreateBiTree(pBiTree *root)
{
char ch = 0;
fflush(stdin);
if ((ch = getchar()) == 'a')//控制樹的結(jié)構(gòu)
{
*root = NULL;
}
else
{
*root = (BiTNode *)malloc(sizeof(BiTNode));
if (!(*root))
{
return RET_ERROR;
}
(*root)->data = GetRandom();
CreateBiTree(&(*root)->leftChild);
CreateBiTree(&(*root)->rightChild);
}
return RET_OK;
}
int g_i = 0;
//創(chuàng)建二叉樹,自動(dòng)執(zhí)行,方便測(cè)試
int CreateBiTreeAuto(pBiTree *root)
{
char szOrder[] = "bbaabaa";
char ch = 0;
if (szOrder[g_i++] == 'a')//控制樹的結(jié)構(gòu)
{
*root = NULL;
}
else
{
*root = (BiTNode *)malloc(sizeof(BiTNode));
if (!(*root))
{
return RET_ERROR;
}
(*root)->data = GetRandom();
CreateBiTreeAuto(&(*root)->leftChild);
CreateBiTreeAuto(&(*root)->rightChild);
}
return RET_OK;
}
三遍歷順序
先序遍歷
先序遍歷是先訪問根結(jié)點(diǎn),再左子樹,再右子樹,比如圖1中的右圖,先序遍歷的輸出如下:
A,B,D,H,I,E,J,K,C,F,G
根據(jù)上面的思想,很容易用遞歸的形式寫出先序遍歷的代碼:
//先序遍歷
int PreOrderVisitTree(pBiTree T, VisitType pFuncVisit)
{
if (T)
{
(*pFuncVisit)(T->data);
if (PreOrderVisitTree(T->leftChild, pFuncVisit) == RET_OK)
{
if (PreOrderVisitTree(T->rightChild, pFuncVisit) == RET_OK)
{
return RET_OK;
}
}
return RET_ERROR;
}
else
{
return RET_OK;
}
}
中序遍歷和后序遍歷
有了先序的經(jīng)驗(yàn),這兩個(gè)就很好理解了,中序是先訪問左子樹, 再根結(jié)點(diǎn),再右子樹, 后序是先訪問左子樹, 再右子樹,再根結(jié)點(diǎn)。代碼更容易,只要改一下調(diào)用順序就可以了。
不過我這里給出一種非遞歸的實(shí)現(xiàn)。遞歸固然是清晰明了,但是存在效率低的問題,非遞歸的方案用棧結(jié)構(gòu)來存結(jié)點(diǎn)信息,通過出棧訪問來遍歷二叉樹。它思想是這樣的,當(dāng)棧頂中的指針非空時(shí),遍歷左子樹,也就是左子樹根的指針進(jìn)棧。當(dāng)棧頂指針為空時(shí),應(yīng)退至上一層,如果是從左子樹返回的,訪問當(dāng)前層,也就是棧頂中的根指針結(jié)點(diǎn)。如果是從右子樹返回,說明當(dāng)前層遍歷完畢,繼續(xù)退棧。代碼如下:
//中序遍歷, 非遞歸實(shí)現(xiàn)
int InOrderVisitTree(pBiTree T, VisitType pFuncVisit)
{
ponyStack binaryTreeStack;
InitStack(&binaryTreeStack, 4);
Push(&binaryTreeStack, &T);
pBiTree pTempNode;
while (!IsEmptyStack(binaryTreeStack))
{
while((GetTop(binaryTreeStack, &pTempNode) == RET_OK) && (pTempNode != NULL))
{
Push(&binaryTreeStack, &(pTempNode->leftChild));
}
Pop(&binaryTreeStack, &pTempNode);
if (!IsEmptyStack(binaryTreeStack))
{
Pop(&binaryTreeStack, &pTempNode);
(*pFuncVisit)(pTempNode->data);
Push(&binaryTreeStack, &(pTempNode->rightChild));
}
}
return RET_OK;
}
代碼下載地址:http://xiazai.jb51.net/201701/yuanma/BinaryTreeDemo-master(jb51.net).rar
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
C語言植物大戰(zhàn)數(shù)據(jù)結(jié)構(gòu)二叉樹遞歸
這篇文章主要為大家介紹了C語言植物大戰(zhàn)數(shù)據(jù)結(jié)構(gòu)二叉樹遞歸,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
C++?OpenCV實(shí)現(xiàn)物體尺寸測(cè)量示例詳解
本文主要介紹了利用OpenCV對(duì)物體的尺寸進(jìn)行測(cè)量,即先定位到待測(cè)物體的位置,然后測(cè)量物體的寬高。感興趣的同學(xué)可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)2022-01-01

