樹形結構是一類重要的非線性數據結構,其中以樹和二叉樹最爲常用。
二叉樹是每個結點最多有兩個子樹的有序樹。通常子樹的根被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用作二叉查找樹和二叉堆或是二叉排序樹。二叉樹的每個結點至多隻有二棵子樹(不存在度大於2的結點),二叉樹的子樹有左右之分,次序不能顛倒。二叉樹的第i層至多有2的 i -1次方個結點;深度爲k的二叉樹至多有2^(k) -1個結點;對任何一棵二叉樹T,如果其終端結點數(即葉子結點數)爲n0,度爲2的結點數爲n2,則n0 = n2 + 1。
二叉樹的鏈式存儲結構是一類重要的數據結構,其形式定義如下:
- //二叉樹結點
- typedef struct BiTNode{
- //數據
- char data;
- //左右孩子指針
- struct BiTNode *lchild,*rchild;
- }BiTNode,*BiTree;
通過讀入一個字符串,建立二叉樹的算法如下:
- //按先序序列創建二叉樹
- int CreateBiTree(BiTree &T){
- char data;
- //按先序次序輸入二叉樹中結點的值(一個字符),‘#’表示空樹
- scanf("%c",&data);
- if(data == '#'){
- T = NULL;
- }
- else{
- T = (BiTree)malloc(sizeof(BiTNode));
- //生成根結點
- T->data = data;
- //構造左子樹
- CreateBiTree(T->lchild);
- //構造右子樹
- CreateBiTree(T->rchild);
- }
- return 0;
- }
二叉樹的遍歷:
遍歷是對樹的一種最基本的運算,所謂遍歷二叉樹,就是按一定的規則和順序走遍二叉樹的所有結點,使每一個結點都被訪問一次,而且只被訪問一次。由於二叉樹是非線性結構,因此,樹的遍歷實質上是將二叉樹的各個結點轉換成爲一個線性序列來表示。
遞歸算法:
- //輸出
- void Visit(BiTree T){
- if(T->data != '#'){
- printf("%c ",T->data);
- }
- }
- //先序遍歷
- void PreOrder(BiTree T){
- if(T != NULL){
- //訪問根節點
- Visit(T);
- //訪問左子結點
- PreOrder(T->lchild);
- //訪問右子結點
- PreOrder(T->rchild);
- }
- }
- //中序遍歷
- void InOrder(BiTree T){
- if(T != NULL){
- //訪問左子結點
- InOrder(T->lchild);
- //訪問根節點
- Visit(T);
- //訪問右子結點
- InOrder(T->rchild);
- }
- }
- //後序遍歷
- void PostOrder(BiTree T){
- if(T != NULL){
- //訪問左子結點
- PostOrder(T->lchild);
- //訪問右子結點
- PostOrder(T->rchild);
- //訪問根節點
- Visit(T);
- }
- }
非遞歸算法:
<1>先序遍歷:
【思路】:訪問T->data後,將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應爲T,出棧,再先序遍歷T的右子樹。
- /* 先序遍歷(非遞歸)
- 思路:訪問T->data後,將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應爲T,出棧,再先序遍歷T的右子樹。
- */
- void PreOrder2(BiTree T){
- stack<BiTree> stack;
- //p是遍歷指針
- BiTree p = T;
- //棧不空或者p不空時循環
- while(p || !stack.empty()){
- if(p != NULL){
- //存入棧中
- stack.push(p);
- //訪問根節點
- printf("%c ",p->data);
- //遍歷左子樹
- p = p->lchild;
- }
- else{
- //退棧
- p = stack.top();
- stack.pop();
- //訪問右子樹
- p = p->rchild;
- }
- }//while
- }
【思路】:T是要遍歷樹的根指針,中序遍歷要求在遍歷完左子樹後,訪問根,再遍歷右子樹。
先將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應爲T,出棧,訪問T->data,再中序遍歷T的右子樹。
- void InOrder2(BiTree T){
- stack<BiTree> stack;
- //p是遍歷指針
- BiTree p = T;
- //棧不空或者p不空時循環
- while(p || !stack.empty()){
- if(p != NULL){
- //存入棧中
- stack.push(p);
- //遍歷左子樹
- p = p->lchild;
- }
- else{
- //退棧,訪問根節點
- p = stack.top();
- printf("%c ",p->data);
- stack.pop();
- //訪問右子樹
- p = p->rchild;
- }
- }//while
- }
<3>後序遍歷
【思路】:T是要遍歷樹的根指針,後序遍歷要求在遍歷完左右子樹後,再訪問根。需要判斷根結點的左右子樹是否均遍歷過。
- //後序遍歷(非遞歸)
- typedef struct BiTNodePost{
- BiTree biTree;
- char tag;
- }BiTNodePost,*BiTreePost;
- void PostOrder2(BiTree T){
- stack<BiTreePost> stack;
- //p是遍歷指針
- BiTree p = T;
- BiTreePost BT;
- //棧不空或者p不空時循環
- while(p != NULL || !stack.empty()){
- //遍歷左子樹
- while(p != NULL){
- BT = (BiTreePost)malloc(sizeof(BiTNodePost));
- BT->biTree = p;
- //訪問過左子樹
- BT->tag = 'L';
- stack.push(BT);
- p = p->lchild;
- }
- //左右子樹訪問完畢訪問根節點
- while(!stack.empty() && (stack.top())->tag == 'R'){
- BT = stack.top();
- //退棧
- stack.pop();
- BT->biTree;
- printf("%c ",BT->biTree->data);
- }
- //遍歷右子樹
- if(!stack.empty()){
- BT = stack.top();
- //訪問過右子樹
- BT->tag = 'R';
- p = BT->biTree;
- p = p->rchild;
- }
- }//while
- }
<4>層次遍歷
【思路】:按從頂向下,從左至右的順序來逐層訪問每個節點,層次遍歷的過程中需要用隊列。
- //層次遍歷
- void LevelOrder(BiTree T){
- BiTree p = T;
- //隊列
- queue<BiTree> queue;
- //根節點入隊
- queue.push(p);
- //隊列不空循環
- while(!queue.empty()){
- //對頭元素出隊
- p = queue.front();
- //訪問p指向的結點
- printf("%c ",p->data);
- //退出隊列
- queue.pop();
- //左子樹不空,將左子樹入隊
- if(p->lchild != NULL){
- queue.push(p->lchild);
- }
- //右子樹不空,將右子樹入隊
- if(p->rchild != NULL){
- queue.push(p->rchild);
- }
- }
- }
測試用例:
輸入:
ABC##DE#G##F###
輸出:
代碼:
- #include<iostream>
- #include<stack>
- #include<queue>
- using namespace std;
- //二叉樹結點
- typedef struct BiTNode{
- //數據
- char data;
- //左右孩子指針
- struct BiTNode *lchild,*rchild;
- }BiTNode,*BiTree;
- //按先序序列創建二叉樹
- int CreateBiTree(BiTree &T){
- char data;
- //按先序次序輸入二叉樹中結點的值(一個字符),‘#’表示空樹
- scanf("%c",&data);
- if(data == '#'){
- T = NULL;
- }
- else{
- T = (BiTree)malloc(sizeof(BiTNode));
- //生成根結點
- T->data = data;
- //構造左子樹
- CreateBiTree(T->lchild);
- //構造右子樹
- CreateBiTree(T->rchild);
- }
- return 0;
- }
- //輸出
- void Visit(BiTree T){
- if(T->data != '#'){
- printf("%c ",T->data);
- }
- }
- //先序遍歷
- void PreOrder(BiTree T){
- if(T != NULL){
- //訪問根節點
- Visit(T);
- //訪問左子結點
- PreOrder(T->lchild);
- //訪問右子結點
- PreOrder(T->rchild);
- }
- }
- //中序遍歷
- void InOrder(BiTree T){
- if(T != NULL){
- //訪問左子結點
- InOrder(T->lchild);
- //訪問根節點
- Visit(T);
- //訪問右子結點
- InOrder(T->rchild);
- }
- }
- //後序遍歷
- void PostOrder(BiTree T){
- if(T != NULL){
- //訪問左子結點
- PostOrder(T->lchild);
- //訪問右子結點
- PostOrder(T->rchild);
- //訪問根節點
- Visit(T);
- }
- }
- /* 先序遍歷(非遞歸)
- 思路:訪問T->data後,將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應爲T,出棧,再先序遍歷T的右子樹。
- */
- void PreOrder2(BiTree T){
- stack<BiTree> stack;
- //p是遍歷指針
- BiTree p = T;
- //棧不空或者p不空時循環
- while(p || !stack.empty()){
- if(p != NULL){
- //存入棧中
- stack.push(p);
- //訪問根節點
- printf("%c ",p->data);
- //遍歷左子樹
- p = p->lchild;
- }
- else{
- //退棧
- p = stack.top();
- stack.pop();
- //訪問右子樹
- p = p->rchild;
- }
- }//while
- }
- /* 中序遍歷(非遞歸)
- 思路:T是要遍歷樹的根指針,中序遍歷要求在遍歷完左子樹後,訪問根,再遍歷右子樹。
- 先將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應爲T,出棧,訪問T->data,再中序遍歷T的右子樹。
- */
- void InOrder2(BiTree T){
- stack<BiTree> stack;
- //p是遍歷指針
- BiTree p = T;
- //棧不空或者p不空時循環
- while(p || !stack.empty()){
- if(p != NULL){
- //存入棧中
- stack.push(p);
- //遍歷左子樹
- p = p->lchild;
- }
- else{
- //退棧,訪問根節點
- p = stack.top();
- printf("%c ",p->data);
- stack.pop();
- //訪問右子樹
- p = p->rchild;
- }
- }//while
- }
- //後序遍歷(非遞歸)
- typedef struct BiTNodePost{
- BiTree biTree;
- char tag;
- }BiTNodePost,*BiTreePost;
- void PostOrder2(BiTree T){
- stack<BiTreePost> stack;
- //p是遍歷指針
- BiTree p = T;
- BiTreePost BT;
- //棧不空或者p不空時循環
- while(p != NULL || !stack.empty()){
- //遍歷左子樹
- while(p != NULL){
- BT = (BiTreePost)malloc(sizeof(BiTNodePost));
- BT->biTree = p;
- //訪問過左子樹
- BT->tag = 'L';
- stack.push(BT);
- p = p->lchild;
- }
- //左右子樹訪問完畢訪問根節點
- while(!stack.empty() && (stack.top())->tag == 'R'){
- BT = stack.top();
- //退棧
- stack.pop();
- BT->biTree;
- printf("%c ",BT->biTree->data);
- }
- //遍歷右子樹
- if(!stack.empty()){
- BT = stack.top();
- //訪問過右子樹
- BT->tag = 'R';
- p = BT->biTree;
- p = p->rchild;
- }
- }//while
- }
- //層次遍歷
- void LevelOrder(BiTree T){
- BiTree p = T;
- //隊列
- queue<BiTree> queue;
- //根節點入隊
- queue.push(p);
- //隊列不空循環
- while(!queue.empty()){
- //對頭元素出隊
- p = queue.front();
- //訪問p指向的結點
- printf("%c ",p->data);
- //退出隊列
- queue.pop();
- //左子樹不空,將左子樹入隊
- if(p->lchild != NULL){
- queue.push(p->lchild);
- }
- //右子樹不空,將右子樹入隊
- if(p->rchild != NULL){
- queue.push(p->rchild);
- }
- }
- }
- int main()
- {
- BiTree T;
- CreateBiTree(T);
- printf("先序遍歷:\n");
- PreOrder(T);
- printf("\n");
- printf("先序遍歷(非遞歸):\n");
- PreOrder2(T);
- printf("\n");
- printf("中序遍歷:\n");
- InOrder(T);
- printf("\n");
- printf("中序遍歷(非遞歸):\n");
- InOrder2(T);
- printf("\n");
- printf("後序遍歷:\n");
- PostOrder(T);
- printf("\n");
- printf("後序遍歷(非遞歸):\n");
- PostOrder2(T);
- printf("\n");
- printf("層次遍歷:\n");
- LevelOrder(T);
- printf("\n");
- return 0;
- }