圖6所示的二叉樹中,每個結點含乙個字元,其非終端結點都是運算子,終端結點都是運算元。請編寫程式,實現對該二叉樹的基本操作。具體操作如表2所示。請自行編寫主程式對各方法進行測試。
圖6 一棵二叉樹t
(1)按先序序列,建立二叉鍊錶:編寫程式,接收t的先序遍歷序列string,根據string,建立t的二叉鍊錶:btnode *creattree(char *string): 其中,btnode為二叉鍊錶的結點結構;string為t的先序遍歷序列。
在輸入的先序序列字串string中,我們使用#來代替為null的樹結點,如上圖中a的左孩子和右孩子就用#代表,按照這個規則,先序遍歷上圖的樹得到的string就為:
-+a##*b##-c##d##/e##f##這也是我們建立樹時要傳遞的字串。
二叉樹我們除了有儲存當前樹結點值的val外,還需要兩個指標left,right,分別指向該根節點的左孩子和右孩子。這裡我們用的是孩子雙親表示法,也可以使用其他的表示方法,如孩子兄弟表示法,left指向當前樹結點的孩子結點,right指向當前樹結點右邊的兄弟結點。
樹結點結構體為:
typedef struct btnodebttree;接著是按照輸入的字串建立二叉鍊錶,因為每乙個樹結點都需要儲存乙個運算子或者運算數,所以使用全域性變數index作為string移動的下標。傳入的字串是按照先序序列輸入的,我們建立樹的時候也按照先序建立。對於字串中每乙個不為#號的字元,開闢樹結點空間,儲存string[index]字元,同時遞迴其左子樹和右子樹,每次遞迴的時候都需要令index遞增。
遞迴結束的條件是遇到的#字元,簡單模擬一下建立樹的過程:
輸入字串為:-+a##*b##-c##d##/e##f##
1,遇到-,開闢樹結點,儲存-,遞迴建立其左子樹和右子樹
2,進入建立其左子樹的過程中
3,遇到+,開闢樹結點,儲存+,遞迴建立其左子樹和右子樹
4,遇到a,開闢樹結點,儲存a,遞迴建立其左子樹和右子樹
5,遇到#,故a的左子樹為null,進入建立a的右子樹的遞迴中
6,遇到#,故a的右子樹為null,進入建立+的右子樹遞迴中
7,遇到*,開闢樹結點,儲存*,遞迴建立其左子樹和右子樹
等等最後返回最開始的根結點,即-的樹結點root
如上,就可以建立出一棵按照輸入的先序序列字串順序的二叉樹
建立過程**為:
#define len sizeof(btnode)(2)二叉樹遍歷:編寫程式,對t進行先序遍歷、中序遍歷、後序遍歷int index=0;
btnode* creattree(char* string)else
return root;
}
void
preorder(btnode *t): 先序遍歷t,輸出其先序序列
void
inorder(btnode *t): 中序遍歷t,輸出其中序序列
void
postorder(btnode *t) : 後序遍歷t,輸出其後序序列
先序遍歷,中序遍歷,後續遍歷,使用遞迴實現的話,思路上是一致的。
從先序遍歷上說,想要輸出一棵二叉樹的先序序列,我們需要知道先序序列的含義,即先輸出根節點,輸出左子樹,再輸出右子樹。對於其左子樹,也是先輸出左子樹的根節點,左子樹的左子樹,再輸出左子樹的右子樹。遞迴點出現,我們只需要按照這個規則遞迴即可,當當前樹結點為空時,就不再繼續遞迴該子樹。當樹根結點的右子樹也遞迴結束後,就代表整棵樹的先序遍歷結束了。
先序遍歷**如下:(對於#因為其是null結點,我們就不再輸出了)
void preorder(btnode* t)else可以看到,輸出的時候,也是按照剛才我們所說的規則輸出,中序遍歷和後序遍歷跟其類似,只需要改變輸出當前樹結點值的位置即可。}
中序遍歷:
void inorder(btnode* t)else後序遍歷:}
void postorder(btnode* t)else(3)求二叉樹的葉子數:編寫程式,求t的葉子數}
intcountleaf(btnode *t);
求二叉樹的葉子數,我們也使用遞迴的方式,想要求得一棵樹的葉子數量,即判斷當前樹結點是否為葉子節點,是的話葉子節點數目+1,否則遞迴計算其左子樹和右子樹上葉子數的和。
是用靜態區域性變數儲存葉子數
int countleaf(btnode* t)else(4)求二叉樹的深度:編寫程式,求t的深度return count;
}
inthigh(btnode *t):編寫程式,求t的深度
還是遞迴的思想,很多問題使用遞迴解決,就會變得很簡單
想要求一棵樹的深度,即求其左子樹和右子樹中,最大的深度+1,+1的原因即算上當前樹結點的深度為1
int max(int a,int b)(5)輸出帶括號的中綴表示式:如果對t進行中序遍歷,將得到乙個中綴表示式,但該表示式沒有小括號,無法體現運算的優先順序。請編寫程式,對二叉樹t,輸出其帶小括號的中綴表示式。int high(btnode* t)else
}
void
infixexpression(btnode *t);
可以發現,我們中序遍歷二叉樹時,結果為:
而在樹的結構中,很明顯是c-d,但是在輸出的結果表示式中卻體現不出這一點,這道題的目的就是讓我們新增括號體現優先順序。
類似於逆波蘭式中運算子優先順序的判定,考慮什麼情況下我們需要新增括號,實際上只有當前樹結點的運算子優先順序大於其左子樹或右子樹根節點的運算子優先順序時,才需要新增括號。而什麼時候當前樹結點的運算子優先順序大於其左子樹或右子樹的根節點運算子優先順序,其實只有一種情況,即當前樹結點為乘號或者除號,而左子樹或右子樹為減號或者加號,這時候才需要新增括號。即便左子樹或右子樹不是運算子,也在上述判斷中不滿足條件,也就是不新增括號。
使用check函式進行優先順序判定
當根結點運算子優先順序高於左子樹或右子樹根結點運算子時,返回true,新增括號
bool check(char a,char b)else因為是在中序遍歷的基礎上新增括號,所以我們也在中序遍歷**的基礎上進行修改}
**為:
void infixexpression(btnode* t)else判定每乙個樹結點與其左右孩子的根節點的優先順序,滿足條件時輸出左括號,遞迴其左子樹,遞迴結束後再輸出右括號。其餘過程與中序遍歷差別不大。infixexpression(temp->left);
if(flag)
} printf("%c",temp->val);
if(temp->right)
infixexpression(temp->right);
if(flag)
} }
}
完整**為:
#include#include#includetypedef struct btnodebttree;在二叉樹的操作中,遞迴是經常會用到也是很重要的思想,需要熟練掌握應用,以及舉一反三。#define len sizeof(btnode)
int max(int a,int b)
void preorder(btnode* t)else
}void inorder(btnode* t)else
}void postorder(btnode* t)else
}int countleaf(btnode* t)else
return count;
}//求樹的深度,即求左子樹和右子樹中的最大深度,出現遞迴點
int high(btnode* t)else
}bool check(char a,char b)else
}//輸出帶括號的中綴表示式,當根結點運算子優先順序高於左子樹或右子樹根結點運算子時,相應左或右子樹前就需要加括號
void infixexpression(btnode* t)else
infixexpression(temp->left);
if(flag)
} printf("%c",temp->val);
if(temp->right)
infixexpression(temp->right);
if(flag)
} }
}int index=0;
btnode* creattree(char* string)else
return root;
}int main()
以上
二叉樹基本操作
tree.h ifndef tree h define tree h include typedef int element 定義二叉樹 typedef struct nodetreenode void preorder treenode root 遞迴前序遍歷 void inorder treen...
二叉樹基本操作
一.二叉樹的定義 二.二叉樹的建立 定義一棵無資料的二叉樹 6 int left size 7 int right size 為了操作簡便,我們定義一棵不需要儲存資料的二叉樹,只要能儲存節點之間的邏輯關係就行,所以用兩個陣列來表示。left i 第i個節點的左子節點的序號 right i 第i個節點...
二叉樹基本操作
include include define maxsize 100 typedef char elemtype typedef struct node btnode void createbtnode btnode b,char str 由str串建立二叉鏈 j ch str j btnode f...