目錄
一,二叉樹的鏈?zhǔn)浇Y(jié)構(gòu)
二,二叉鏈的接口實(shí)現(xiàn)
????????1,二叉鏈的創(chuàng)建
????????2,接口函數(shù)
????????3,動(dòng)態(tài)創(chuàng)立新結(jié)點(diǎn)
????????4,創(chuàng)建二叉樹
????????5,前序遍歷
????????6,中序遍歷
????????7,后序遍歷
三,結(jié)點(diǎn)個(gè)數(shù)以及高度等
????????1,接口函數(shù)
????????2,結(jié)點(diǎn)個(gè)數(shù)
????????3,葉子結(jié)點(diǎn)個(gè)數(shù)
????????4,二叉樹高度
????????5,二叉樹第k層結(jié)點(diǎn)個(gè)數(shù)
????????6,二叉樹查找值為x的結(jié)點(diǎn)
一,二叉樹的鏈?zhǔn)浇Y(jié)構(gòu)
二叉樹的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)是指,用鏈表來表示一棵二叉樹,即用鏈來指示元素的邏輯關(guān)系;
通常的方法是鏈表中每個(gè)結(jié)點(diǎn)由三個(gè)域組成,數(shù)據(jù)域和左右指針域,左右指針分別用來給出該結(jié)點(diǎn)左孩子和右孩子所在的鏈結(jié)點(diǎn)的存儲(chǔ)地址 。
鏈?zhǔn)浇Y(jié)構(gòu)又分為二叉鏈和三叉鏈,這里我們學(xué)習(xí)二叉鏈;
?二叉樹是:
1,空樹
2,非空:根節(jié)點(diǎn),根節(jié)點(diǎn)的左子樹、根節(jié)點(diǎn)的右子樹組成的。
從圖示中可以看出,二叉樹定義是遞歸式的也稱遞歸樹,因此后序基本操作中基本都是按照該概念實(shí)現(xiàn)的;
?二叉鏈結(jié)構(gòu)圖示;
二,二叉鏈的接口實(shí)現(xiàn)
????????1,二叉鏈的創(chuàng)建
typedef int BTDataType;
//二叉鏈
typedef struct BinaryTreeNode
{
BTDataType data; // 當(dāng)前結(jié)點(diǎn)值域
struct BinaryTreeNode* left; // 指向當(dāng)前結(jié)點(diǎn)左孩子
struct BinaryTreeNode* right; // 指向當(dāng)前結(jié)點(diǎn)右孩子
}BTNode;
首先創(chuàng)建一個(gè)結(jié)構(gòu)體表示二叉鏈,data是當(dāng)前結(jié)點(diǎn)的值域,BTDataType是儲(chǔ)存的值的數(shù)據(jù)類型;
left是指向當(dāng)前結(jié)點(diǎn)左孩子,right是指向當(dāng)前結(jié)點(diǎn)右孩子;
這里的BTDataType是int的重命名,也可以說是數(shù)據(jù)類型的重命名,這樣統(tǒng)一化方便后續(xù)更改;
????????2,接口函數(shù)
//動(dòng)態(tài)創(chuàng)立新結(jié)點(diǎn)
BTNode* BuyNode(BTDataType x);
//創(chuàng)建二叉樹
BTNode* GreatBTree();
//前序遍歷
void PrevOrder(BTNode* root);
//中序遍歷
void InOrder(BTNode* root);
//后序遍歷
void PostOrder(BTNode* root);
這是以上要實(shí)現(xiàn)的接口函數(shù);
????????3,動(dòng)態(tài)創(chuàng)立新結(jié)點(diǎn)
//動(dòng)態(tài)創(chuàng)立新結(jié)點(diǎn)
BTNode* BuyNode(BTDataType x)
{
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
assert(newnode);
newnode->data = x;
newnode->left = NULL;
newnode->right = NULL;
return newnode;
}
后面創(chuàng)立新結(jié)點(diǎn)時(shí)直接調(diào)用此函數(shù),一定要向堆區(qū)申請(qǐng)空間,這樣函數(shù)結(jié)束空間會(huì)保留不會(huì)被回收;
將data賦新值,left與right都指向空,再返回結(jié)點(diǎn)指針即可;
????????4,創(chuàng)建二叉樹
//創(chuàng)建二叉樹
BTNode* GreatBTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
return node1;
}
然后我們申請(qǐng)結(jié)點(diǎn)來構(gòu)造二叉樹,通過鏈接將新結(jié)點(diǎn)鏈接起來;
創(chuàng)建的二叉樹結(jié)構(gòu)圖示如下:
????????5,前序遍歷
//前序遍歷
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
二叉樹的前序,中序,后續(xù)遍歷都是同一種思路:
1. 前序遍歷(Preorder Traversal 亦稱先序遍歷)——根結(jié)點(diǎn)---->左子樹--->右子樹
2. 中序遍歷(Inorder Traversal)——左子樹--->根結(jié)點(diǎn)--->右子樹
3. 后序遍歷(Postorder Traversal)——左子樹--->右子樹--->根結(jié)點(diǎn)
這里要用到遞歸思想:這里NULL用N表示,建議畫圖來理解,一層一層遍歷下去;
前序遍歷:
先訪問根結(jié)點(diǎn)(1)然后訪問其左子樹(2);打印 1
此時(shí)根結(jié)點(diǎn)為(2)然后訪問其左子樹(3);打印1 2
此時(shí)根結(jié)點(diǎn)為(3)然后訪問其左子樹(NULL);打印1 2 3
此時(shí)根結(jié)點(diǎn)為(NULL)return NULL到(3),然后訪問(3)的右子樹(NULL);打印1 2 3 N
此時(shí)根結(jié)點(diǎn)為(NULL)return NULL到(3),此時(shí)對(duì)(3)也就是對(duì)(2)的左子樹的訪問結(jié)束了,然后訪問(2)的右子樹(NULL);打印1 2 3 N N
此時(shí)根結(jié)點(diǎn)為(NULL)return NULL到(2),此時(shí)對(duì)(2)也就是對(duì)(1)的左子樹訪問結(jié)束了,然后訪問(1)的右子樹(4);打印1 2 3 N N N
此時(shí)根結(jié)點(diǎn)為(4)然后訪問其左子樹(5);打印1 2 3 N N N 4
此時(shí)根結(jié)點(diǎn)為(5)然后訪問其左子樹(NULL);打印1 2 3 N N N 4 5
此時(shí)根結(jié)點(diǎn)為(NULL)return NULL到(5)然后訪問(5)的右子樹(NULL);打印1 2 3 N N N 4 5 N
此時(shí)根結(jié)點(diǎn)為(NULL)return NULL到(5)此時(shí)對(duì)(5)也就是對(duì)(4)的左子樹的訪問結(jié)束了,然后訪問(4)的右子樹(6);打印 1 2 3 N N N 4 5 N N
此時(shí)根結(jié)點(diǎn)為(6)然后訪問其左子樹(NULL);打印1 2 3 N N N 4 5 N N 6
此時(shí)根結(jié)點(diǎn)為(NULL)return NULL到(6)然后訪問(6)的右子樹(NULL);打印1 2 3 N N N 4 5 N N 6 N
此時(shí)根結(jié)點(diǎn)為(NULL)return NULL到(6),此時(shí)對(duì)(6)也就是對(duì)(4)的右子樹的訪問結(jié)束了,此時(shí)對(duì)(4)也就是對(duì)(1)的右子樹的訪問結(jié)束了,此時(shí)對(duì)(1)的訪問也結(jié)束了,前序遍歷也就結(jié)束了;打印1 2 3 N N N 4 5 N N 6 N N
圖解思路示例:
? ??
BTNode* root = GreatBTree();
//前序遍歷
PrevOrder(root);
這就是前序遍歷;
????????6,中序遍歷
//中序遍歷
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
中序遍歷:左子樹--->根結(jié)點(diǎn)--->右子樹
跟前序遍歷思路一致,就是換了一下訪問的順序,按照前序遍歷的思路來就完事了;
//中序遍歷
InOrder(root);
printf("\n");
????????7,后序遍歷
//后序遍歷
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
后序遍歷:左子樹--->右子樹--->根結(jié)點(diǎn)
思路還是一致的,就是換了一下訪問順序,前,中,后序遍歷的思路都是一致的,只要搞清楚其中一個(gè)就全部拿捏了;
//后續(xù)遍歷
PostOrder(root);
printf("\n");
?這里對(duì)二叉鏈的基礎(chǔ)遍歷就實(shí)現(xiàn)完全了,有人說還有一個(gè)層序遍歷,這個(gè)遍歷需要用到隊(duì)列,目前C語言階段實(shí)現(xiàn)太過于繁瑣,后序博主會(huì)補(bǔ)上;
三,結(jié)點(diǎn)個(gè)數(shù)以及高度等
像此類問題也都是遞歸問題,更加看重我們對(duì)函數(shù)棧幀的理解;
????????1,接口函數(shù)
//結(jié)點(diǎn)個(gè)數(shù)
int SumNode(BTNode* root);
//葉子結(jié)點(diǎn)個(gè)數(shù)
int LeafNode(BTNode* root);
//二叉樹高度
int HeightTree(BTNode* root);
//二叉樹第k層結(jié)點(diǎn)個(gè)數(shù)
int BTreeLeveSize(BTNode* root, int k);
//二叉樹查找值為x的結(jié)點(diǎn)
BTNode* BTreeFine(BTNode* root, int x);
以上是要實(shí)現(xiàn)的函數(shù);
????????2,結(jié)點(diǎn)個(gè)數(shù)
//結(jié)點(diǎn)個(gè)數(shù)
int SumNode(BTNode* root)
{
return root == NULL ? 0 : SumNode(root->left) + SumNode(root->right) + 1;
}
遞歸其實(shí)說難也難,說不難也不難,是有技巧在里面的;
1,大事化小:根結(jié)點(diǎn)為(1)的二叉樹的結(jié)點(diǎn)總和==>左子樹(2)的結(jié)點(diǎn)總和加上右子樹(4)的結(jié)點(diǎn)總和再加上本身的結(jié)點(diǎn)個(gè)數(shù)1,然后根結(jié)點(diǎn)為(2)的結(jié)點(diǎn)總和==>左子樹(3)的總和加上NULL加1,這就是規(guī)律;【(1)=(2)+(4)+1 】
2,結(jié)束條件,當(dāng)結(jié)點(diǎn)為NULL時(shí)返回0;
//結(jié)點(diǎn)個(gè)數(shù)
printf("%d\n", SumNode(root));
????????3,葉子結(jié)點(diǎn)個(gè)數(shù)
//葉子結(jié)點(diǎn)個(gè)數(shù)
int LeafNode(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left==NULL && root->right==NULL)
{
return 1;
}
else
{
return LeafNode(root->left) + LeafNode(root->right);
}
}
?
大事化小:求根結(jié)點(diǎn)為(1)的二叉樹的葉子節(jié)點(diǎn)的個(gè)數(shù)==>其左子樹(2)加上其右子樹(4)的葉子節(jié)點(diǎn)的個(gè)數(shù);【(1)=(2)+(4)】
結(jié)束條件:當(dāng)結(jié)點(diǎn)為NULL時(shí)返回0,當(dāng)結(jié)點(diǎn)的左右子樹都為NULL時(shí)返回1;
????????4,二叉樹高度
//二叉樹高度
int HeightTree(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int left = HeightTree(root->left);
int right = HeightTree(root->right);
return left > right ? left + 1 : right + 1;
}
大事化小:求根結(jié)點(diǎn)為(1)的二叉樹的高度==>其左子樹(2)與右子樹(4)中高的一顆的高度加上本身的高度1;【(1)=(2)>(4)?(2)+1:(4)+1 】
結(jié)束條件:當(dāng)結(jié)點(diǎn)為NULL時(shí)返回0;
//二叉樹高度
printf("%d\n", HeightTree(root));
????????5,二叉樹第k層結(jié)點(diǎn)個(gè)數(shù)
//二叉樹第k層結(jié)點(diǎn)個(gè)數(shù)
int BTreeLeveSize(BTNode* root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BTreeLeveSize(root->left, k - 1) + BTreeLeveSize(root->right, k - 1);
}
大事化小:求根結(jié)點(diǎn)為(1)的二叉樹第K層的結(jié)點(diǎn)個(gè)數(shù)==>其左子樹(2)加上右子樹(4)中第K-1層結(jié)點(diǎn)的個(gè)數(shù);【(1)=(2)+(4)】
結(jié)束條件:當(dāng)結(jié)點(diǎn)為NULL時(shí)返回0,當(dāng)K等于1時(shí)返回1;
//二叉樹第k層結(jié)點(diǎn)個(gè)數(shù)
printf("%d\n", BTreeLeveSize(root,3));
????????6,二叉樹查找值為x的結(jié)點(diǎn)
//二叉樹查找值為x的結(jié)點(diǎn)
BTNode* BTreeFine(BTNode* root, int x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
if (BTreeFine(root->left, x) == NULL)
{
return BTreeFine(root->right, x);
}
else
{
return BTreeFine(root->left, x);
}
}
大事化小:查找根結(jié)點(diǎn)為(1)的二叉樹中值為x的結(jié)點(diǎn)==>查找其左子樹(2)與右子樹(4)中值為x的結(jié)點(diǎn);
結(jié)束條件:當(dāng)結(jié)點(diǎn)為NULL時(shí)返回NULL,當(dāng)結(jié)點(diǎn)的值為x時(shí)返回該結(jié)點(diǎn);
思路:所以當(dāng)其中一個(gè)子樹不為NULL時(shí)就是所求的結(jié)點(diǎn),如果左子樹不為空則返回左子樹的結(jié)點(diǎn),否則返回右子樹的結(jié)點(diǎn),如果左右都為空那也返回右子樹的結(jié)點(diǎn);
//二叉樹查找值為x的結(jié)點(diǎn)
BTNode* ret = BTreeFine(root, 6);
printf("%d\n", ret->data);
ret = BTreeFine(root, 3);
printf("%d\n", ret->data);
到這里就結(jié)束了,通過這些題目也充分的認(rèn)識(shí)了二叉樹(遞歸樹),這就是遞歸算法,還是要多畫圖來理解,遞歸基層的知識(shí)就是函數(shù)棧幀的創(chuàng)建與銷毀;
第三階段就到這里了,這階段帶大家了解一下二叉樹(遞歸樹)的遞歸思想;
后面博主會(huì)陸續(xù)更新;
如有不足之處歡迎來補(bǔ)充交流!文章來源:http://www.zghlxwxcb.cn/news/detail-720957.html
完結(jié)。。文章來源地址http://www.zghlxwxcb.cn/news/detail-720957.html
到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】二叉樹鏈?zhǔn)浇Y(jié)構(gòu)的實(shí)現(xiàn)(三)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!