文章目錄
前言
一、二叉樹(shù)是什么?
二、二叉樹(shù)的創(chuàng)建(以順序表的形式)
1.先創(chuàng)建數(shù)組(為了順序表做準(zhǔn)備)
2.輸入數(shù)值
3.輸出數(shù)值
4.創(chuàng)建空二叉樹(shù)并在二叉樹(shù)中輸入數(shù)值
5.判斷是否為空二叉樹(shù)
6.返回二叉樹(shù)的深度
7.先序遍歷輸出二叉樹(shù)
8.中序遍歷輸出二叉樹(shù)
9.后序遍歷輸出二叉樹(shù)
10.層序遍歷輸出二叉樹(shù)
11.總結(jié):(全部代碼實(shí)現(xiàn))
三、二叉樹(shù)的基本操作(鏈表版本)
1.鏈表節(jié)點(diǎn)結(jié)構(gòu)體
2.先序創(chuàng)建二叉樹(shù)
1)中序后序創(chuàng)建二叉樹(shù)
3.先序遍歷二叉樹(shù)
1)遞歸操作
2)非遞歸操作
4.中序遍歷二叉樹(shù)
1)遞歸操作
2)非遞歸操作
5.后序遍歷二叉樹(shù)
1)遞歸操作
2)非遞歸操作
6.求第k層節(jié)點(diǎn)的個(gè)數(shù)
7.求二叉樹(shù)的深度
8.二叉樹(shù)的葉子節(jié)點(diǎn)的個(gè)數(shù)
9.二叉樹(shù)的總節(jié)點(diǎn)的個(gè)數(shù)
10.遞歸查找指定元素x的節(jié)點(diǎn)
11.全部代碼:
總結(jié)
本文主要內(nèi)容:
?1.對(duì)于二叉樹(shù)的基本操作,創(chuàng)建 、遍歷 、前序、中序、后序輸出等
?2.關(guān)于二叉樹(shù)的自己的體會(huì)
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
二叉樹(shù)是數(shù)據(jù)結(jié)構(gòu)中的一種,可以通過(guò)順序表或者鏈表的形式實(shí)現(xiàn),接下來(lái)讓我們一起來(lái)看看如何通過(guò)順序表或者鏈表的形式來(lái)實(shí)現(xiàn)二叉樹(shù)
二叉樹(shù)圖:
??
二、二叉樹(shù)的創(chuàng)建(以順序表的形式) 1.先創(chuàng)建數(shù)組(為了順序表做準(zhǔn)備)代碼如下(示例):
#define MAX_SIZE 100
typedef char TElemType;
typedef TElemType SqBiTree[MAX_SIZE];
TElemType Nil = '#'; 我們規(guī)定當(dāng)元素?cái)?shù)值為 ‘#’ 的時(shí)候表示該節(jié)點(diǎn)為NULL
2.輸入數(shù)值代碼如下(示例):
void input(TElemType* x) {
?? ?scanf("%c", &x);
} 簡(jiǎn)單明了的傳參 TElemType也就是char類型的變量 x
3.輸出數(shù)值void visit(TElemType x) {
printf("%c", x);
}
4.創(chuàng)建空二叉樹(shù)并在二叉樹(shù)中輸入數(shù)值void InitBiTree(SqBiTree T) {
//構(gòu)造空二叉樹(shù)
for (int i = 0; i< MAX_SIZE; i++) {
T[i] = Nil;//初始值為空 #表示為空
}
}
void CreateBiTree(SqBiTree T) {
int i = 1;//按照層次次序輸入二叉樹(shù)中結(jié)點(diǎn)的值
scanf("%s", T + 1);
while (T[i]!='\0')
{
i++;
}
T[i] = '#';
}
5.判斷是否為空二叉樹(shù)int BiTreeEmpty(SqBiTree T) {
//初始條件:二叉樹(shù)T存在。操作結(jié)果為:若T為空二叉樹(shù),則返回true 否則false
if (T[1] == Nil) {
return 1;//空
}
else {
return 0;
}
}
6.返回二叉樹(shù)的深度//返回二叉樹(shù)的深度
int BiTreeDepth(SqBiTree T) {
int i = MAX_SIZE - 1;
while (T[i] == '#') {
i--;
}
//現(xiàn)在得到的i是存儲(chǔ)元素的最后一位
int j = i;
int dep = 0;
while (j >= 1) {
dep++;
j = j / 2;//根據(jù)的是樹(shù)的深度為 【log2n】+1
}
return dep;
}
7.先序遍歷輸出二叉樹(shù)這里的先序遍歷是通過(guò)二叉樹(shù)的一個(gè)性質(zhì)來(lái)輸出的,二叉樹(shù)的性質(zhì)之一:把完全二叉樹(shù)的節(jié)點(diǎn)按照順序?qū)懞脤?duì)應(yīng)坐標(biāo)之后,i 節(jié)點(diǎn)的左右孩子的坐標(biāo)位置分別為 2 * i? 和? 2 * i + 1;
void PreTraverse(SqBiTree T, int e) {
if (T[e] != '#') {
visit(T[e]);
PreTraverse(T, 2 * e);
PreTraverse(T, 2 * e + 1);//遍歷輸出結(jié)點(diǎn) 左子樹(shù) 右子樹(shù)
}
} 這個(gè)函數(shù)就涉及到二叉樹(shù)的一個(gè)性質(zhì) 通過(guò)2*e和2*e+1來(lái)進(jìn)行遍歷
void PreOrderTraverse(SqBiTree T) {
//先序遍歷
if (!BiTreeEmpty(T)) {
//非空就調(diào)用PreTraverse
PreTraverse(T, 1);//從第一個(gè)元素就開(kāi)始
}
printf("\n");
}
8.中序遍歷輸出二叉樹(shù)void InTraverse(SqBiTree T, int e) {
//中序遍歷使用
if (T[e] != '#') {
InTraverse(T, 2 * e);
visit(T[e]);
InTraverse(T, 2 * e + 1);//中序遍歷,先左后中再右
}
}
void InOrderTreaverse(SqBiTree T) {
if (!BiTreeEmpty(T)) {
InTraverse(T, 1);
}
printf("\n");
}
9.后序遍歷輸出二叉樹(shù)//后序遍歷
void PostTreaverse(SqBiTree T, int e) {
if (T[e] != '#') {
PostTreaverse(T, e * 2);
PostTreaverse(T, e * 2 + 1);
visit(T[e]);
}
}
void PostOrderTraverse(SqBiTree T) {
if (!BiTreeEmpty(T)){
PostTreaverse(T, 1);
}
printf("\n");
}
10.層序遍歷輸出二叉樹(shù)//層序遍歷二叉樹(shù)
void LevelOrderTraverse(SqBiTree T) {
int dep = BiTreeDepth(T);
int tree_max = pow(dep, 2) - 1;//得到當(dāng)前深度下完全二叉樹(shù)應(yīng)該有多少結(jié)點(diǎn)
for (int i = 1; i< tree_max; i++) {
if (T[i] == '#') {
continue;
}//不用輸出‘#’ 而且獲得的深度來(lái)看,不需要for很多沒(méi)有價(jià)值的 下標(biāo) 牛逼
visit(T[i]);
}
}
11.總結(jié):(全部代碼實(shí)現(xiàn))#define _CRT_SECURE_NO_WARNINGS
#include#include#include#include//使用順序結(jié)構(gòu)完成二叉樹(shù)的基本操作
//編寫(xiě)前序、中序、后序以及層次順序遍歷二叉樹(shù)的算法,并計(jì)算二叉樹(shù)的深度、所有的結(jié)點(diǎn)數(shù)
#define MAX_SIZE 100
typedef char TElemType;
typedef TElemType SqBiTree[MAX_SIZE];
TElemType Nil = '#';
void input(TElemType* x) {
scanf("%c", &x);
}
void visit(TElemType x) {
printf("%c", x);
}
void InitBiTree(SqBiTree T) {
//構(gòu)造空二叉樹(shù)
for (int i = 0; i< MAX_SIZE; i++) {
T[i] = Nil;//初始值為空 #表示為空
}
}
void CreateBiTree(SqBiTree T) {
int i = 1;//按照層次次序輸入二叉樹(shù)中結(jié)點(diǎn)的值
scanf("%s", T + 1);
while (T[i]!='\0')
{
i++;
}
T[i] = '#';
}
int BiTreeEmpty(SqBiTree T) {
//初始條件:二叉樹(shù)T存在。操作結(jié)果為:若T為空二叉樹(shù),則返回true 否則false
if (T[1] == Nil) {
return 1;//空
}
else {
return 0;
}
}
//返回二叉樹(shù)的深度
int BiTreeDepth(SqBiTree T) {
int i = MAX_SIZE - 1;
while (T[i] == '#') {
i--;
}
//現(xiàn)在得到的i是存儲(chǔ)元素的最后一位
int j = i;
int dep = 0;
while (j >= 1) {
dep++;
j = j / 2;//根據(jù)的是樹(shù)的深度為 【log2n】+1
}
return dep;
}
void PreTraverse(SqBiTree T, int e) {
if (T[e] != '#') {
visit(T[e]);
PreTraverse(T, 2 * e);
PreTraverse(T, 2 * e + 1);//遍歷輸出結(jié)點(diǎn) 左子樹(shù) 右子樹(shù)
}
}
void PreOrderTraverse(SqBiTree T) {
//先序遍歷
if (!BiTreeEmpty(T)) {
//非空就調(diào)用PreTraverse
PreTraverse(T, 1);//從第一個(gè)元素就開(kāi)始
}
printf("\n");
}
void InTraverse(SqBiTree T, int e) {
//中序遍歷使用
if (T[e] != '#') {
InTraverse(T, 2 * e);
visit(T[e]);
InTraverse(T, 2 * e + 1);//中序遍歷,先左后中再右
}
}
void InOrderTreaverse(SqBiTree T) {
if (!BiTreeEmpty(T)) {
InTraverse(T, 1);
}
printf("\n");
}
//后序遍歷
void PostTreaverse(SqBiTree T, int e) {
if (T[e] != '#') {
PostTreaverse(T, e * 2);
PostTreaverse(T, e * 2 + 1);
visit(T[e]);
}
}
void PostOrderTraverse(SqBiTree T) {
if (!BiTreeEmpty(T)){
PostTreaverse(T, 1);
}
printf("\n");
}
//層序遍歷二叉樹(shù)
void LevelOrderTraverse(SqBiTree T) {
int dep = BiTreeDepth(T);
int tree_max = pow(dep, 2) - 1;//得到當(dāng)前深度下完全二叉樹(shù)應(yīng)該有多少結(jié)點(diǎn)
for (int i = 1; i< tree_max; i++) {
if (T[i] == '#') {
continue;
}//不用輸出‘#’ 而且獲得的深度來(lái)看,不需要for很多沒(méi)有價(jià)值的 下標(biāo) 牛逼
visit(T[i]);
}
}
int main()
{
TElemType e;
SqBiTree Bt;
InitBiTree(Bt);
CreateBiTree(Bt);
printf("先序遍歷結(jié)果為:");
PreOrderTraverse(Bt);
printf("中序遍歷結(jié)果為:");
InOrderTreaverse(Bt);
printf("后序遍歷結(jié)果為:");
PostOrderTraverse(Bt);
LevelOrderTraverse(Bt);
return 0;
}
三、二叉樹(shù)的基本操作(鏈表版本)
1.鏈表節(jié)點(diǎn)結(jié)構(gòu)體//使用鏈表的 形式來(lái)實(shí)現(xiàn)二叉樹(shù)的基本操作
#define MaxSize 20
typedef struct TreeNode {
int data;//數(shù)據(jù)域
struct TreeNode* lchild;
struct TreeNode* rchild;//指向左右孩子結(jié)點(diǎn)
}BiNode,*BiTree;
2.先序創(chuàng)建二叉樹(shù)//先序創(chuàng)建二叉樹(shù)
BiTree CreateTree() {
int data;
scanf("%d", &data); //這樣每一次輸入都會(huì)進(jìn)行傳值操作 直到輸入的數(shù)值小于0然后開(kāi)始右結(jié)點(diǎn) 使用遞歸的方法進(jìn)行創(chuàng)建二叉樹(shù)
BiTree root;
if (data<= 0) {
return NULL;
}
else {
root = (BiTree)malloc(sizeof(BiNode));
root->data = data;
root->lchild = CreateTree();
root->rchild = CreateTree();//先序傳參 制造先序二叉樹(shù)
}
return root;
}
1)中序后序創(chuàng)建二叉樹(shù)?中序、后序創(chuàng)建二叉樹(shù)的代碼實(shí)現(xiàn)就是更改先序創(chuàng)建二叉樹(shù)中的??root->data = data;? 位置
更改遞歸的先后就可以,到這個(gè)地方大家應(yīng)該就明白如何操作了(我看了一圈的csdn也沒(méi)有多少人去說(shuō)明這個(gè)事情,這是我在群里問(wèn)了其他人的回復(fù):更換遞歸和root->data = data;的位置)
3.先序遍歷二叉樹(shù) 1)遞歸操作所有的二叉樹(shù)的遍歷,不管先序、中序、后序,都可以通過(guò)遞歸的形式是實(shí)現(xiàn)。這個(gè)是最簡(jiǎn)單,最好理解的方式。在不強(qiáng)調(diào)不能使用遞歸的前提下,建議優(yōu)先使用遞歸。
先序、中序、后序,只是跟上文那個(gè)什么序進(jìn)行遍歷創(chuàng)建二叉樹(shù)一個(gè)道理,更改遞歸的位置即可~
//先序遍歷
//1.遞歸操作 實(shí)現(xiàn)先序遍歷
void PreTreeTraverse(BiTree root) {
if (root) {
//只要根節(jié)點(diǎn)不是空
printf("%d", root->data);
PreTreeTraverse(root->lchild);
PreTreeTraverse(root->rchild);
}
}
2)非遞歸操作非遞歸操作中,先序、中序、后序操作都有不同,所以這個(gè)比較難理解和記憶,但是有一個(gè)共同點(diǎn)是,都需要使用輔助棧的形式(定義一個(gè)類似于棧的 BiTree數(shù)組)?。?!
這個(gè)寫(xiě)法大概思路是創(chuàng)建輔助棧,然后創(chuàng)建新節(jié)點(diǎn),然后判斷是否根節(jié)點(diǎn)為NULL(NULL的話不需要 遍歷直接返回),設(shè)置棧數(shù)組的變量top? 提前賦值為-1(任意都可以? 按照個(gè)人習(xí)慣來(lái)),然后是top++(top現(xiàn)在為0)將root賦值給stack[0]? 然后進(jìn)入while循環(huán)(判斷依據(jù)是棧中有沒(méi)有元素 top>-1),因?yàn)槭窍刃蛩灾苯虞敵鰎oot節(jié)點(diǎn)數(shù)據(jù),然后使用 if 語(yǔ)句判斷是否有左右孩子(先判斷右孩子的原因是,使得最后一個(gè)放進(jìn)去的是左孩子節(jié)點(diǎn),這樣出棧的時(shí)候,是左孩子先出)
//2.非遞歸操作
//引入輔助棧
void PreOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root != NULL) {
//表示非空樹(shù)
top++;
stack[top] = root;//將root放在stack數(shù)組中
//棧不空的時(shí)候循環(huán)
while (top >-1) { //這個(gè)寫(xiě)法牛逼的地方在于 首先是先輸出根結(jié)點(diǎn)的數(shù)值然后出棧top--然后判斷是否有右孩子,放進(jìn)棧里面 然后
//出棧并訪問(wèn)結(jié)點(diǎn) //這個(gè)時(shí)候如果有孩子的話 top>-1 還能進(jìn)入while循環(huán) 然后輸出左孩子(有的話)然后出棧 在判斷這個(gè)孩子有沒(méi)有左右孩子,一旦是沒(méi)有了,你們前面壓棧的右孩子就可以輸出了
//輸出右孩子之后,出棧 然后進(jìn)行判斷右孩子是由有左右孩子 模擬遞歸操縱s
p = stack[top];
top--;
printf("%d", p->data);
//右孩子入棧
if (p->rchild != NULL) {
top++;
stack[top] = p->rchild;
}
//左孩子入棧
if (p->lchild != NULL) {
top++;
stack[top] = p->rchild;
}
}
}
}
4.中序遍歷二叉樹(shù)
1)遞歸操作這個(gè)時(shí)候就能發(fā)現(xiàn),有一定的規(guī)律在遞歸操作上
//有了先序遍歷操作就可以寫(xiě)出來(lái)中序和后序遍歷操作
//2.中序遍歷
//1)遞歸操作
void InTreeTreaver(BiTree root) {
if (root) {
//判斷根節(jié)點(diǎn)是否為空
InTreeTreaver(root->lchild);
printf("%d", root->data);
InTreeTreaver(root->rchild);
}
}
2)非遞歸操作//2)非遞歸
void InTreeTreaver2(BiTree root) {
//創(chuàng)建臨時(shí)棧
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root) {
p = root;
while (top >-1 || p != NULL) {
//掃描所有的左孩子進(jìn)棧 其實(shí)在左孩子進(jìn)棧的過(guò)程中就只剩下右孩子
while (p != NULL) {
top++;
stack[top] = p;
p = p->lchild;
}
//掃描完成之后開(kāi)始出棧
if (top >-1) {
p = stack[top];
printf("%d", p->data);
top--;
//掃描右孩子
p = p->rchild;
}
}
}
}
5.后序遍歷二叉樹(shù)
1)遞歸操作//3.后序遍歷
void PostTreeTraverse(BiTree root) {
if (root) {
PostTreeTraverse(root->lchild);
PostTreeTraverse(root->rchild);
printf("%d", root->data);
}
}
2)非遞歸操作//2)非遞歸
//后序遍歷二叉樹(shù):非遞歸實(shí)現(xiàn)
void PostOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
int sign;
if (root != NULL) {
do {
//root節(jié)點(diǎn)入棧
while (root != NULL) {
top++;
stack[top] = root;
root = root->lchild;
}
//p指向棧頂前一個(gè)已訪問(wèn)節(jié)點(diǎn)
p = NULL;
//置root為已訪問(wèn)
sign = 1;
while (top != -1 && sign) {
//取出棧頂節(jié)點(diǎn)
root = stack[top];
//右孩子不存在或右孩子已訪問(wèn)則訪問(wèn)root
if (root->rchild == p) {
printf("%d ", root->data);
top--;
//p指向被訪問(wèn)的節(jié)點(diǎn)
p = root;
}
else {
//root指向右孩子節(jié)點(diǎn)
root = root->rchild;
//置未訪問(wèn)標(biāo)記
sign = 0;
}
}
} while (top != -1);
}
}
6.求第k層節(jié)點(diǎn)的個(gè)數(shù)/遇到葉子節(jié)點(diǎn) ?正好 ?因?yàn)槟阆胍氖堑趉層 所以就k-1遞歸 然后等你到第k層的時(shí)候也就是k=1的時(shí)候 自然就返回1 了
你輸入的第k層,在遞歸k-1次之后到達(dá)第k層,自然就返回1了 ,然后就得到對(duì)應(yīng)第k層的數(shù)值了
int LevelNodeNum(BiTree root,int k) {
//進(jìn)行判斷是否為空 或者k數(shù)值不對(duì)
if (root == NULL || k< 1) {
return 0;
}
if (k == 1) {
return 1;//遇到葉子節(jié)點(diǎn) 正好 因?yàn)槟阆胍氖堑趉層 所以就k-1遞歸 然后等你到第k層的時(shí)候也就是k=1的時(shí)候 自然就返回1 了
}
else {
return LevelNodeNum(root->lchild, k - 1) + LevelNodeNum(root->rchild,k - 1);
}
}
7.求二叉樹(shù)的深度//求二叉樹(shù)的大深度
//使用遞歸的方式來(lái)實(shí)現(xiàn)
int maxTreeDeapth(BiTree root) {
if (root) {
int maxLeft = maxTreeDeapth(root->lchild);
int maxRight = maxTreeDeapth(root->rchild);
if (maxLeft >maxRight) {
return maxLeft + 1;
}
else {
return maxRight + 1;
}
}
return 0;
}
8.二叉樹(shù)的葉子節(jié)點(diǎn)的個(gè)數(shù)//求二叉樹(shù)葉子節(jié)點(diǎn)個(gè)數(shù)
int LeafNodeNum(BiTree root) {
//這一步是為了判斷是否為空樹(shù) 空樹(shù)自然葉子節(jié)點(diǎn)為0
if (root == NULL) {
return 0;
}
if (root->lchild == NULL && root->rchild == NULL) {
return 1;//葉子節(jié)點(diǎn)的特點(diǎn) 沒(méi)有孩子 所以用遞歸 可以得到葉子節(jié)點(diǎn)的個(gè)數(shù)
}
else {
return LeafNodeNum(root->lchild) + LeafNodeNum(root->rchild);
}
}
9.二叉樹(shù)的總節(jié)點(diǎn)的個(gè)數(shù)求二叉樹(shù)的葉子節(jié)點(diǎn)的個(gè)數(shù)和求總結(jié)點(diǎn)的個(gè)數(shù),不同的是在使用遞歸的時(shí)候,如果是為了求葉子節(jié)點(diǎn)只需要在這個(gè)節(jié)點(diǎn)沒(méi)有左右孩子的時(shí)候 return 1? 在遞歸的時(shí)候不會(huì)+1
但是在求總結(jié)點(diǎn)個(gè)數(shù)的時(shí)候,使用遞歸的時(shí)候都會(huì)+1,這個(gè)+1表示的是加上這個(gè)節(jié)點(diǎn)了,其他方面沒(méi)有不同
//二叉樹(shù)的總節(jié)點(diǎn)個(gè)數(shù)
//使用遞歸的方法實(shí)現(xiàn)
int CountNode(BiTree root) {
if (root) {
if (root->lchild == NULL && root->rchild == NULL) {
return 1;//遇到葉子節(jié)點(diǎn)返回1
}
else {
return CountNode(root->lchild) + CountNode(root->rchild)+1;
//每一次正常運(yùn)行都會(huì)加一并表示節(jié)點(diǎn),直到遇到葉子節(jié)點(diǎn)返回?cái)?shù)值1
}
}
}
10.遞歸查找指定元素x的節(jié)點(diǎn)先進(jìn)行左孩子遞歸一直找,找不到的時(shí)候就會(huì)使用右孩子遞歸了,如果再找不到就返回NULL,表示沒(méi)有這個(gè)元素為x的節(jié)點(diǎn)
//查找元素為x的節(jié)點(diǎn)
//采用遞歸的方法
BiTree SearchNode(BiTree root, int x) {
if (root) {//這個(gè)是判斷是否為NULL空樹(shù) 第二是為了判斷左右孩子是否為NULL
if (root->data == x) {
return root;//如果數(shù)據(jù)對(duì)了就返回相應(yīng)節(jié)點(diǎn)
}
else {
BiTree p;//創(chuàng)建新節(jié)點(diǎn)位置
p = SearchNode(root->lchild, x);//使用遍歷 先左孩子進(jìn)行比較
if (!p) {//左孩子比較完成之后在進(jìn)行右孩子遞歸比較 只要有有孩子就可以一直比較
p = SearchNode(root->rchild, x);
}
return p;
}
}
return NULL;
}
11.全部代碼:#define _CRT_SECURE_NO_WARNINGS
#include#include#include#include//使用鏈表的 形式來(lái)實(shí)現(xiàn)二叉樹(shù)的基本操作
#define MaxSize 20
typedef struct TreeNode {
int data;//數(shù)據(jù)域
struct TreeNode* lchild;
struct TreeNode* rchild;//指向左右孩子結(jié)點(diǎn)
}BiNode,*BiTree;
//我們規(guī)定,節(jié)點(diǎn)值必須為大于0的數(shù)值 ,如果是0表示奇數(shù)繼續(xù)往下創(chuàng)建子節(jié)點(diǎn)的操作
//二叉樹(shù)的操作通常使用遞歸的方法,二叉樹(shù)的操作可以分為兩類,一類是需要改變二叉樹(shù)的結(jié)構(gòu),比如二叉樹(shù)的創(chuàng)建,結(jié)點(diǎn)的刪除等等
//這類操作,傳入的二叉樹(shù)的 結(jié)點(diǎn)參數(shù)為二叉樹(shù)指針的地址,這種參數(shù)的傳入,便于更改二叉樹(shù)結(jié)構(gòu)體的指針
//我們使用遞歸的方法創(chuàng)建左右子樹(shù)
//第一種創(chuàng)建二叉樹(shù),使用一級(jí)指針
//先序創(chuàng)建二叉樹(shù)
BiTree CreateTree() {
int data;
scanf("%d", &data); //這樣每一次輸入都會(huì)進(jìn)行傳值操作 直到輸入的數(shù)值小于0然后開(kāi)始右結(jié)點(diǎn) 使用遞歸的方法進(jìn)行創(chuàng)建二叉樹(shù)
BiTree root;
if (data<= 0) {
return NULL;
}
else {
root = (BiTree)malloc(sizeof(BiNode));
root->data = data;
root->lchild = CreateTree();
root->rchild = CreateTree();//先序傳參 制造先序二叉樹(shù)
}
return root;
}
//先序遍歷
//1.遞歸操作 實(shí)現(xiàn)先序遍歷
void PreTreeTraverse(BiTree root) {
if (root) {
//只要根節(jié)點(diǎn)不是空
printf("%d", root->data);
PreTreeTraverse(root->lchild);
PreTreeTraverse(root->rchild);
}
}
//2.非遞歸操作
//引入輔助棧
void PreOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root != NULL) {
//表示非空樹(shù)
top++;
stack[top] = root;//將root放在stack數(shù)組中
//棧不空的時(shí)候循環(huán)
while (top >-1) { //這個(gè)寫(xiě)法牛逼的地方在于 首先是先輸出根結(jié)點(diǎn)的數(shù)值然后出棧top--然后判斷是否有右孩子,放進(jìn)棧里面 然后
//出棧并訪問(wèn)結(jié)點(diǎn) //這個(gè)時(shí)候如果有孩子的話 top>-1 還能進(jìn)入while循環(huán) 然后輸出左孩子(有的話)然后出棧 在判斷這個(gè)孩子有沒(méi)有左右孩子,一旦是沒(méi)有了,你們前面壓棧的右孩子就可以輸出了
//輸出右孩子之后,出棧 然后進(jìn)行判斷右孩子是由有左右孩子 模擬遞歸操縱s
p = stack[top];
top--;
printf("%d", p->data);
//右孩子入棧
if (p->rchild != NULL) {
top++;
stack[top] = p->rchild;
}
//左孩子入棧
if (p->lchild != NULL) {
top++;
stack[top] = p->rchild;
}
}
}
}
//有了先序遍歷操作就可以寫(xiě)出來(lái)中序和后序遍歷操作
//2.中序遍歷
//1)遞歸操作
void InTreeTreaver(BiTree root) {
if (root) {
//判斷根節(jié)點(diǎn)是否為空
InTreeTreaver(root->lchild);
printf("%d", root->data);
InTreeTreaver(root->rchild);
}
}
//2)非遞歸
void InTreeTreaver2(BiTree root) {
//創(chuàng)建臨時(shí)棧
BiTree stack[MaxSize];
BiTree p;
int top = -1;
if (root) {
p = root;
while (top >-1 || p != NULL) {
//掃描所有的左孩子進(jìn)棧 其實(shí)在左孩子進(jìn)棧的過(guò)程中就只剩下右孩子
while (p != NULL) {
top++;
stack[top] = p;
p = p->lchild;
}
//掃描完成之后開(kāi)始出棧
if (top >-1) {
p = stack[top];
printf("%d", p->data);
top--;
//掃描右孩子
p = p->rchild;
}
}
}
}
//3.后序遍歷
void PostTreeTraverse(BiTree root) {
if (root) {
PostTreeTraverse(root->lchild);
PostTreeTraverse(root->rchild);
printf("%d", root->data);
}
}
//2)非遞歸
//后序遍歷二叉樹(shù):非遞歸實(shí)現(xiàn)
void PostOrderTraverseNonRec(BiTree root) {
BiTree stack[MaxSize];
BiTree p;
int top = -1;
int sign;
if (root != NULL) {
do {
//root節(jié)點(diǎn)入棧
while (root != NULL) {
top++;
stack[top] = root;
root = root->lchild;
}
//p指向棧頂前一個(gè)已訪問(wèn)節(jié)點(diǎn)
p = NULL;
//置root為已訪問(wèn)
sign = 1;
while (top != -1 && sign) {
//取出棧頂節(jié)點(diǎn)
root = stack[top];
//右孩子不存在或右孩子已訪問(wèn)則訪問(wèn)root
if (root->rchild == p) {
printf("%d ", root->data);
top--;
//p指向被訪問(wèn)的節(jié)點(diǎn)
p = root;
}
else {
//root指向右孩子節(jié)點(diǎn)
root = root->rchild;
//置未訪問(wèn)標(biāo)記
sign = 0;
}
}
} while (top != -1);
}
}
//求二叉樹(shù)的大深度
//使用遞歸的方式來(lái)實(shí)現(xiàn)
int maxTreeDeapth(BiTree root) {
if (root) {
int maxLeft = maxTreeDeapth(root->lchild);
int maxRight = maxTreeDeapth(root->rchild);
if (maxLeft >maxRight) {
return maxLeft + 1;
}
else {
return maxRight + 1;
}
}
return 0;
}
//求二叉樹(shù)葉子節(jié)點(diǎn)個(gè)數(shù)
int LeafNodeNum(BiTree root) {
//這一步是為了判斷是否為空樹(shù) 空樹(shù)自然葉子節(jié)點(diǎn)為0
if (root == NULL) {
return 0;
}
if (root->lchild == NULL && root->rchild == NULL) {
return 1;//葉子節(jié)點(diǎn)的特點(diǎn) 沒(méi)有孩子 所以用遞歸 可以得到葉子節(jié)點(diǎn)的個(gè)數(shù)
}
else {
return LeafNodeNum(root->lchild) + LeafNodeNum(root->rchild);
}
}
////// 求k層節(jié)點(diǎn)個(gè)數(shù)
/// /// int LevelNodeNum(BiTree root,int k) {
//進(jìn)行判斷是否為空 或者k數(shù)值不對(duì)
if (root == NULL || k< 1) {
return 0;
}
if (k == 1) {
return 1;//遇到葉子節(jié)點(diǎn) 正好 因?yàn)槟阆胍氖堑趉層 所以就k-1遞歸 然后等你到第k層的時(shí)候也就是k=1的時(shí)候 自然就返回1 了
}
else {
return LevelNodeNum(root->lchild, k - 1) + LevelNodeNum(root->rchild,k - 1);
}
}
//二叉樹(shù)的總節(jié)點(diǎn)個(gè)數(shù)
//使用遞歸的方法實(shí)現(xiàn)
int CountNode(BiTree root) {
if (root) {
if (root->lchild == NULL && root->rchild == NULL) {
return 1;//遇到葉子節(jié)點(diǎn)返回1
}
else {
return CountNode(root->lchild) + CountNode(root->rchild)+1;
//每一次正常運(yùn)行都會(huì)加一并表示節(jié)點(diǎn),直到遇到葉子節(jié)點(diǎn)返回?cái)?shù)值1
}
}
}
//查找元素為x的節(jié)點(diǎn)
//采用遞歸的方法
BiTree SearchNode(BiTree root, int x) {
if (root) {//這個(gè)是判斷是否為NULL空樹(shù) 第二是為了判斷左右孩子是否為NULL
if (root->data == x) {
return root;//如果數(shù)據(jù)對(duì)了就返回相應(yīng)節(jié)點(diǎn)
}
else {
BiTree p;//創(chuàng)建新節(jié)點(diǎn)位置
p = SearchNode(root->lchild, x);//使用遍歷 先左孩子進(jìn)行比較
if (!p) {//左孩子比較完成之后在進(jìn)行右孩子遞歸比較 只要有有孩子就可以一直比較
p = SearchNode(root->rchild, x);
}
return p;
}
}
return NULL;
}
int main()
{
BiTree root = NULL;
root = CreateTree();
PreTreeTraverse(root);
printf("\n");
int len = maxTreeDeapth(root);
printf("%d",len);
printf("\n");
int num=LevelNodeNum(root, 2);
printf("%d\n", num);
printf("%d\n", CountNode(root));
return 0;
}
1.? 對(duì)于二叉樹(shù)的學(xué)習(xí),我已經(jīng)使用過(guò)了順序表的形式和鏈表的形式,感覺(jué)上,第一次在二叉樹(shù)上發(fā)現(xiàn)了遞歸的神奇用處,之前使用的遞歸的時(shí)候都是一些很小的問(wèn)題的解決,這一次在二叉樹(shù)的遍歷上面,無(wú)論是如何實(shí)現(xiàn)的二叉樹(shù),在進(jìn)行先序、中序、后序等遍歷的時(shí)候,使用遞歸的方法是最為簡(jiǎn)單,也是最好理解的。
2.雖然在學(xué)過(guò)Java之后覺(jué)得C為基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)真的好麻煩,Java會(huì)有相應(yīng)的框架,一時(shí)畏難心理就有了,但是在最近的學(xué)習(xí)來(lái)看,數(shù)據(jù)結(jié)構(gòu)是一門很有意思的課程,在這個(gè)地方,感受到數(shù)據(jù)結(jié)構(gòu)的魅力,希望大家不要畏難,感受敲完代碼理解代碼的那一刻的快樂(lè),大家加油!??!
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧