實現二叉樹的ADT需要分別實現結點ADT和樹ADT,同時也要保證其封裝性
二叉鏈表
樹結點ADT的聲明以及實現
(1)每一個結點包括其要儲存的數據以及左右子節點的指針,通常一顆二叉樹中只有根結點能被直接訪問,所以要把數據以及子節點的指針設為private
(2)成員函數要包括:
- 構造函數,析構函數(也可以不寫)
- 獲取和設置結點內儲存的數據,左子節點,右子節點
- 判斷該節點是否為葉子節點
1 #ifndef _BINNODE_HPP_ 2 #define _BINNODE_HPP_ 3 #include<iostream> 4 using namespace std; 5 class BinNode{ 6 private: 7 char data; 8 BinNode *lc; 9 BinNode *rc; 10 public: 11 BinNode(char dataval,BinNode *l=NULL,BinNode* r=NULL){data=dataval;lc=l;rc=r;} 12 BinNode(BinNode *l=NULL,BinNode* r=NULL){lc=l;rc=r;} 13 void setLeftChild(BinNode *l){lc=l;} 14 void setRightChild(BinNode *r){rc=r;} 15 void setdata(char d){data=d;} 16 BinNode *getlc(){return lc;} 17 BinNode *getrc(){return rc;} 18 char getdata(){return data;} 19 bool isLeaf(){return lc==NULL&&rc==NULL;} 20 }; 21 #endif
樹ADT的聲明
成員變量:根結點
基本操作:獲取樹的高度,結點數目,前序/中序/后序遍歷,設置根節點的值,撤銷整棵樹
1 #ifndef _BINTREE_HPP_ 2 #define _BINTREE_HPP_ 3 #include "BinNode.hpp" 4 #include <iostream> 5 using namespace std; 6 class BinTree{ 7 public: 8 BinNode * root; 9 int depth(BinNode*); 10 int count(BinNode*); 11 void setroot(BinNode*); 12 void clear(BinNode*); 13 void preorder(BinNode*,void(*visit)(BinNode*)); 14 void inorder(BinNode*,void(*visit)(BinNode*)); 15 void postorder(BinNode*,void(*visit)(BinNode*)); 16 }; 17 #endif
樹ADT的實現
樹很多操作都需要用到遞歸,學會運用遞歸是實現這些基本操作的必要條件
而遞歸可以使用棧來模擬,所以只要理解好了棧就很好理解遞歸操作了,這里就不贅述了
前序遍歷:根節點->左子樹->右子樹
中序遍歷:左子樹->根節點->右子樹
后序遍歷:左子樹->右子樹->根節點
這三種遍歷實現方法大同小異,不同點就在於訪問的先后順序不同罷了
簡單的描述一下過程(嘗試用棧的知識去理解,先進先出):把遞歸函數看作是一個元素,若要執行某一層的遞歸函數,則將該函數入棧,如果函數執行過程中碰到終止條件則終止函數進程即彈棧,反復進行直到棧為空,即所有的函數都被執行完
獲取樹的高度以及結點的個數也要使用遞歸,代碼的也大同小異
#include "BinTree.hpp" void BinTree::preorder(BinNode* r,void (*visit)(BinNode* c)){ if(r==NULL)return ; visit(r); preorder(r->getlc(),visit); preorder(r->getrc(),visit); } void BinTree::inorder(BinNode* r,void (*visit)(BinNode* c)){ if(r==NULL)return ; inorder(r->getlc(),visit); visit(r); inorder(r->getrc(),visit); } void BinTree::postorder(BinNode* r,void (*visit)(BinNode* c)){ if(r==NULL)return ; postorder(r->getlc(),visit); postorder(r->getrc(),visit); visit(r); } void BinTree::setroot(BinNode* r){ root=r; } void BinTree::clear(BinNode*r){ if(r==NULL)return; clear(r->getlc()); clear(r->getrc()); delete r; } int BinTree::depth(BinNode* r){ int lh=0,rh=0; if(r!=NULL) { lh=depth(r->getlc()); rh=depth(r->getrc()); return (lh>rh?lh:rh)+1; } else return 0; } int BinTree::count(BinNode*r){ if(r==NULL)return 0; return (count(r->getlc())+count(r->getrc()))+1; }
demo程序
此部分首先碰到的問題就是如何構造一個二叉樹了,構造二叉樹有很多種方法,這里采用前序遍歷的方法來實現
1 #include"BinTree.hpp" 2 #include"BinTree.cpp" 3 #include<iostream> 4 #include<stdio.h> 5 #include<stdlib.h> 6 using namespace std; 7 BinNode* r; 8 BinTree T; 9 char s[1000]; 10 int cnt=0; 11 void visit(BinNode*r); 12 BinNode * creatTree(char c); 13 void input(); 14 int main() 15 { 16 input(); 17 printf("輸出樹的深度:"); 18 printf("%d",T.depth(r)); 19 printf("\n"); 20 printf("輸出樹的結點個數:"); 21 printf("%d",T.count(r)); 22 printf("\n"); 23 printf("按前序遍歷輸出結果\n"); 24 T.preorder(r,visit); 25 printf("\n"); 26 printf("按中序遍歷輸出結果\n"); 27 T.inorder(r,visit); 28 printf("\n"); 29 printf("按后序遍歷輸出結果\n"); 30 T.postorder(r,visit); 31 printf("\n"); 32 return 0; 33 } 34 void visit(BinNode*r){ 35 printf("%c",r->getdata()); 36 } 37 BinNode * creatTree(char c){ 38 BinNode* temp; 39 if(c=='/') temp=NULL; 40 else 41 { 42 temp=new BinNode; 43 temp->setdata(c); 44 c=s[++cnt]; 45 temp->setLeftChild(creatTree(c)); 46 c=s[++cnt]; 47 temp->setRightChild(creatTree(c)); 48 } 49 T.setroot(temp); 50 return temp; 51 } 52 void input(){ 53 printf("輸入的注意事項\n"); 54 printf("(1)使用前序遍歷的順序輸入\n"); 55 printf("(2)空節點以'/'替代\n"); 56 printf("(3)輸入只有一行\n"); 57 printf("(4)輸入的數據類型都為char\n"); 58 printf("(5)輸入樣例:AB/D//CEG///FH//I//\n\n"); 59 printf("請輸入二叉樹\n"); 60 scanf("%s",s); 61 r=creatTree(s[0]); 62 }
左子節點右兄弟節點
樹節點ADT
成員變量:節點存儲的數據,左子節點的指向,父節點的指向,右兄弟節點的指向
成員函數:跟二叉鏈表很相似,都包括構造函數,獲取,設置函數,但是少了判斷是否為葉子節點的函數,因為僅僅根據以上已知的東西無法判斷是否為葉子節點
1 #ifndef _BINNODE_HPP_ 2 #define _BINNODE_HPP_ 3 #include <iostream> 4 using namespace std; 5 class BinNode{ 6 private: 7 char data; 8 int parent; 9 int lc; 10 int rightbro; 11 public: 12 BinNode(){data='/';parent=-1;lc=-1;rightbro=-1;} 13 void setdata(char d){data=d;} 14 void setparent(int p){parent=p;} 15 void setLc(int l){lc=l;} 16 void setRightbro(int r){rightbro=r;} 17 char getData(){return data;} 18 int getParent(){return parent;} 19 int getLc(){return lc;} 20 int getRightbro(){return rightbro;} 21 }; 22 #endif
樹ADT的聲明
與二叉鏈表大同小異,只是實現的方法不同罷了,這里不多加贅述了
1 #ifndef _BINTREE_HPP_ 2 #define _BINTREE_HPP_ 3 #include "BinNode.hpp" 4 #include <iostream> 5 using namespace std; 6 class BinTree{ 7 public: 8 BinNode node[1024+10]; 9 int depth(int ); 10 int count(int ); 11 void preorder(int ,void(*visit)(BinNode no)); 12 void inorder(int ,void(*visit)(BinNode no)); 13 void postorder(int ,void(*visit)(BinNode no)); 14 }; 15 #endif
樹ADT的實現
這里注意一個問題:
遍歷操作的終止條件多了一項,因為左子節點右兄弟節點使用數組來實現的,所以數組的下標有很大的作用,稍不注意就可能越界
1 #include "BinTree.hpp" 2 void BinTree::preorder(int n,void(*visit)(BinNode no)){ 3 if(node[n].getData()=='/'||n==-1)return; 4 visit(node[n]); 5 preorder(node[n].getLc(),visit); 6 preorder(node[node[n].getLc()].getRightbro(),visit); 7 } 8 void BinTree::inorder(int n,void(*visit)(BinNode no)){ 9 if(node[n].getData()=='/'||n==-1)return; 10 inorder(node[n].getLc(),visit); 11 visit(node[n]); 12 inorder(node[node[n].getLc()].getRightbro(),visit); 13 } 14 void BinTree::postorder(int n,void(*visit)(BinNode no)){ 15 if(node[n].getData()=='/'||n==-1)return; 16 postorder(node[n].getLc(),visit); 17 postorder(node[node[n].getLc()].getRightbro(),visit); 18 visit(node[n]); 19 } 20 int BinTree::count(int n){ 21 if(node[n].getData()=='/'||n==-1)return 0; 22 return count(node[n].getLc())+count(node[node[n].getLc()].getRightbro())+1; 23 } 24 int BinTree::depth(int n){ 25 if(node[n].getData()=='/'||n==-1)return 0; 26 int lh=depth(node[n].getLc()); 27 int rh=depth(node[node[n].getLc()].getRightbro()); 28 return (lh>rh?lh:rh)+1; 29 }
demo程序
(1)在構造一顆二叉樹時使用的是按層次遍歷的順序,使用其他的順序都不好構造
(2)在構造函數的時候,注意可以利用二叉樹的性質,比如左節點的下標一定是奇數,左子節點的下標與父節點的下標之間的關系,兄弟節點之間下標之間的關系
弄清楚這些后就很容易的按照層次遍歷的順序構造好一顆二叉樹
我比較懶,這里就沒有考慮父節點的指向了
1 #include "BinTree.hpp" 2 #include "BinTree.cpp" 3 #include<iostream> 4 #include<stdio.h> 5 #include<stdlib.h> 6 #include<cstring> 7 using namespace std; 8 char s[1000]; 9 int cnt=0; 10 BinTree T; 11 int n=0,m=0; 12 void visit(BinNode n){ 13 printf("%c",n.getData()); 14 } 15 void creatTree(BinTree &t){ 16 for(int i=0;i<=1024;i++){ 17 t.node[i].setdata('/'); 18 } 19 for(int i=0;s[i]!='\0';i++){ 20 t.node[i].setdata(s[i]); 21 if(s[i]=='/')continue; 22 t.node[i].setLc((2*i)+1); 23 if((i+1)%2==0&&i!=0)t.node[i].setRightbro(i+1); 24 } 25 } 26 void input(){ 27 printf("輸入的注意事項\n"); 28 printf("(1)使用層次遍歷的順序輸入\n"); 29 printf("(2)空節點以'/'替代\n"); 30 printf("(3)輸入只有一行\n"); 31 printf("(4)輸入的數據類型都為char\n"); 32 printf("(5)輸入的樹的結點要么是葉子結點要么必有左子結點\n"); 33 printf("(7)輸入樣例ABCD/E/FG//HI//\n\n"); 34 printf("請輸入二叉樹\n"); 35 scanf("%s",s); 36 creatTree(T); 37 } 38 int main() 39 { 40 input(); 41 printf("輸出樹的深度:"); 42 printf("%d",T.depth(0)); 43 printf("\n"); 44 printf("輸出樹的結點個數:"); 45 printf("%d",T.count(0)); 46 printf("\n"); 47 printf("按前序遍歷輸出結果\n"); 48 T.preorder(0,visit); 49 printf("\n"); 50 printf("按中序遍歷輸出結果\n"); 51 T.inorder(0,visit); 52 printf("\n"); 53 printf("按后序遍歷輸出結果\n"); 54 T.postorder(0,visit); 55 printf("\n"); 56 return 0; 57 }