基于左子结点/右兄弟结点表示法和二叉链表来实现二叉树ADT
实现二叉树的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 }