第6章树与森林 第6章树与森林 6-1写出用广义表表示法表示的树的类声明,并给出如下成员函数的实现: (1) operator>>()接收用广义表表示的树作为输入,建立广义表的存储表示; (2)复制构造函数用另一棵表示为广义表的树初始化一棵树 (3) operator=()测试用广义表表示的两棵树是否相等 (4) operator<<()用广义表的形式输出一棵树; (5)析构函数清除一棵用广义表表示的树。 【解答】 #include <iostream. h> #define maxSub TreeNum 20 最大子树(子表)个数 class EntRee: ∥ Gen tree类的前视声明 lass GenTreeNode i 广义树结点类的声明 private 结点标志 Gen TreeNode s ne /dtpe=0,指向第一个 /tpe=1或2,指向同 ∥联合 char rootdata: type=0,根结点数据 har Childdata: /tpe=1,子女结点数据 Gen TreeNode *firstchild type=2,指向第一个子女的指针 public: if (tp=0)RootData= item; else ChildData=item; J ∥构造函数:构造广义表表示的树的数据结点 Gen TreeNode( GenTreeNode *son= NULL): utype(2), nextSibling (NULL), firstChild( son)U ∥构造函数:构造广义表表示的树的子树结点 ∥返回结点的数据类型 char GetData(i return data; 返回数据结点的值 GenTreeNode"GetFchild (return firstChild: j ∥返回子树结点的地址 历返回下一个兄弟结点的地址 void setlnfo( char item)( data=item; 3 ∥将结点中的值修改为item void set Child( Gen TreeNode *ptr)( firstChild=ptr; 1 ∥将结点中的子树指针修改为p void setNsinbilg( Gen TreeNode ptr)(nextSibling=ptr; 3 class entRee i ∥广义树类定义 private: en TreeNode "first; ∥根指针
第 6 章 树与森林 66 第 6 章 树与森林 6-1 写出用广义表表示法表示的树的类声明,并给出如下成员函数的实现: (1) operator >> ( ) 接收用广义表表示的树作为输入,建立广义表的存储表示; (2) 复制构造函数 用另一棵表示为广义表的树初始化一棵树; (3) operator == ( ) 测试用广义表表示的两棵树是否相等; (4) operator << ( ) 用广义表的形式输出一棵树; (5) 析构函数 清除一棵用广义表表示的树。 【解答】 #include <iostream.h> #define maxSubTreeNum 20; //最大子树(子表)个数 class GenTree; //GenTree 类的前视声明 class GenTreeNode { //广义树结点类的声明 friend class GenTree; private: int utype; //结点标志:=0, 数据; =1, 子女 GenTreeNode * nextSibling; //utype=0, 指向第一个子女; //utype=1 或 2, 指向同一层下一兄弟 union { //联合 char RootData; //utype=0, 根结点数据 char Childdata; //utype=1, 子女结点数据 GenTreeNode *firstChild; //utype=2, 指向第一个子女的指针 } public: GenTreeNode ( int tp, char item ) : utype (tp), nextSibling (NULL) { if ( tp == 0 ) RootData = item; else ChildData = item; } //构造函数:构造广义表表示的树的数据结点 GenTreeNode ( GenTreeNode *son = NULL ) : utype (2), nextSibling (NULL), firstChild ( son ) { } //构造函数:构造广义表表示的树的子树结点 int nodetype ( ) { return utype; } //返回结点的数据类型 char GetData ( ) { return data; } //返回数据结点的值 GenTreeNode * GetFchild ( ) { return firstChild; } //返回子树结点的地址 GenTreeNode * GetNsibling ( ) { return nextSibling; } //返回下一个兄弟结点的地址 void setInfo ( char item ) { data = item; } //将结点中的值修改为 item void setFchild ( GenTreeNode * ptr ) { firstChild = ptr; } //将结点中的子树指针修改为 ptr void setNsinbilg ( GenTreeNode * ptr ) { nextSibling = ptr; } }; class GenTree { //广义树类定义 private: GenTreeNode *first; //根指针
第6章树与森林 建树时的停止输入标志 ∥复制一个pt指示的子树 void Traverse( GenListNode*ptr ) ∥对p为根的子树遍历并输出 /将以p为根的广义树结构释放 friend int Equal( Gen TreeNode*s, GenTreeNodet ) ∥比较以s和t为根的树是否相等 ublic 构造函数 EntRee(; ∥析构函数 ∥判两棵树t与是否相等 friend istream& operator >> istream& in, Gen Tree& t); 输入 friend ostream& operator << ostream& out, Gen Tree& t ) 输出 (1) operator>>()接收用广义表表示的树作为输入,建立广义表的存储表示 istream& operator >> istream& in, Gen Tree&t)i ∥友元函数,从输入流对象i接受用广义表表示的树,建立广义表的存储表示t。 t. ConstructTree( in, ret Value ) return in: void Gen Tree: ConstructTree( istream& in, char& value )i ∥从输入流对象n接受用广义表表示的非空树,建立广义表的存储表示te Stack <GenTreeNode*>st(max SubTreeNum); 用于建表时记忆回退地址 Gen TreeNode*p, q, r; Type ch cin > value ∥广义树停止输入标志数据 first=q= new GenTreeNode(0, ch ) ∥建立整个树的根结点 if( ch==()st Push(q) ∥接着应是`(,进栈 switch( ch)& case( new GenTreeNode <Type>(q); ∥建立子树,p-> firstchild=q r=st GetTop(: st Pop(; ∥从栈中退出前一结点 r->nextsibling ∥插在前一结点r之后 stPush(p ) st Push( q ) ∥子树结点及子树根结点进栈 case): q->nextSibling= NULL; st pop(; ∥子树建成,封闭链,退到上层 if( st Is Empty (==0)q=st GetTop(); ∥栈不空,取上层链子树结点 else return 0: ∥栈空,无上层链,算法结東 ir( isupper(ch)q= new Gen TreeNode(0,ch);∥大写字母,建根结点
第 6 章 树与森林 67 char retValue; //建树时的停止输入标志 GenTreeNode *Copy ( GenTreeNode * ptr ); //复制一个 ptr 指示的子树 void Traverse ( GenListNode * ptr ); //对 ptr 为根的子树遍历并输出 void Remove ( GenTreeNode *ptr ); //将以 ptr 为根的广义树结构释放 friend int Equal ( GenTreeNode *s, GenTreeNode *t ); //比较以 s 和 t 为根的树是否相等 public: GenTree ( ); //构造函数 GenTree ( GenTree& t ); //复制构造函数 ~GenTree ( ); //析构函数 friend int operator == ( GenTree& t1, GenTree& t2 ); //判两棵树 t1 与 t2 是否相等 friend istream& operator >> ( istream& in, GenTree& t ); //输入 friend ostream& operator << ( ostream& out, GenTree& t ); //输出 } (1) operator >> ( ) 接收用广义表表示的树作为输入,建立广义表的存储表示 istream& operator >> ( istream& in, GenTree& t ) { //友元函数, 从输入流对象 in 接受用广义表表示的树,建立广义表的存储表示 t。 t.ConstructTree ( in, retValue ); return in; } void GenTree :: ConstructTree ( istream& in, char& value ) { //从输入流对象 in 接受用广义表表示的非空树,建立广义表的存储表示 t。 Stack <GenTreeNode* > st (maxSubTreeNum); //用于建表时记忆回退地址 GenTreeNode * p, q, r; Type ch; cin >> value; //广义树停止输入标志数据 cin >> ch; first = q = new GenTreeNode ( 0, ch ); //建立整个树的根结点 cin >> ch; if ( ch == ‘(’ ) st.Push ( q ); //接着应是‘(’, 进栈 cin >> ch; while ( ch != value ) { //逐个结点加入 switch ( ch ) { case ‘(’ : p = new GenTreeNode <Type> ( q ); //建立子树, p->firstChild = q r = st.GetTop( ); st.Pop( ); //从栈中退出前一结点 r->nextSibling = p; //插在前一结点 r 之后 st.Push( p ); st.Push( q ); //子树结点及子树根结点进栈 break; case ‘)’ : q->nextSibling = NULL; st.pop( ); //子树建成, 封闭链, 退到上层 if ( st.IsEmpty ( ) == 0 ) q = st.GetTop( ); //栈不空, 取上层链子树结点 else return 0; //栈空, 无上层链, 算法结束 break; case ‘,’ : break; default : p = q; if ( isupper (ch) ) q = new GenTreeNode ( 0, ch ); //大写字母, 建根结点
第6章树与森林 else q=new GenTreeNode( 1, ch ) ∥排大写字母,建数据结点 (2)复制构造函数用另一棵表示为广义表的树初始化一棵树 GenTree: GenTree( const GenTree& t)i ∥共有函数 GenTreeNode* Gen Tree Copy( Gen TreeNode"ptr)t ∥私有函数,复制一个p指示的用广义表表示的子树 Gen TreeNode * q=NUll q= new Gen TreeNode( ptr->utype, NULL ) 传送值域 case 0: q->RootData= ptr-> RootData: break ∥传送根结点数据 case 1: q->Child Data= ptr->ChildData: break; ∥传送子女结点数据 case2:q-> firstchild=Copy(pr-> firstchild); break;∥递归传送子树信息 q->nextsibling= Copy( ptr->nexts ibling ) ∥复制同一层下一结点为头的表 return q; (3) operator==()测试用广义表表示的两棵树是否相等 int operator==( Gen Tree& tl, Gen Tree& 22) ∥友元函数:判两棵树t与是否相等,若两表完全相等,函数返回1,否则返回0 return Equal(tl. first, t2. first ) int Equal( Gen TreeNode tl, GenTreeNode t2)i 是 GenTreeNode的友元函数 if( tI ==NULL &&t2=NULL)return 1: ∥表t1与表都是空树,相等 f(t=NUL&&t2l=NULL&&tl->upe=口2->utpe){W两子树都非空且结点类型相同 switch( tl->utype ∥比较对应数据 case0: x=(tI->RootData==t2->RootData)? 1: 0; ∥根数据结点 se 1: x=(tI->ChildData ==t2->ChildData)? 1: 0; ∥)子女数据结点
第 6 章 树与森林 68 else q = new GenTreeNode ( 1, ch ); //非大写字母, 建数据结点 p->nextSibling = q; //链接 } cin >> ch; } } (2) 复制构造函数 用另一棵表示为广义表的树初始化一棵树; GenTree :: GenTree ( const GenTree& t ) { //共有函数 first = Copy ( t.first ); } GenTreeNode* GenTree :: Copy ( GenTreeNode *ptr ) { //私有函数,复制一个 ptr 指示的用广义表表示的子树 GenTreeNode *q = NULL; if ( ptr != NULL ) { q = new GenTreeNode ( ptr->utype, NULL ); switch ( ptr->utype ) { //根据结点类型 utype 传送值域 case 0 : q->RootData = ptr->RootData; break; //传送根结点数据 case 1 : q->ChildData = ptr->ChildData; break; //传送子女结点数据 case 2 : q->firstChild = Copy ( ptr->firstChild ); break; //递归传送子树信息 } q->nextSibling = Copy ( ptr->nextSibling ); //复制同一层下一结点为头的表 } return q; } (3) operator == ( ) 测试用广义表表示的两棵树是否相等 int operator == ( GenTree& t1, GenTree& t2 ) { //友元函数 : 判两棵树 t1 与 t2 是否相等, 若两表完全相等, 函数返回 1, 否则返回 0。 return Equal ( t1.first, t2.first ); } int Equal ( GenTreeNode *t1, GenTreeNode *t2 ) { //是 GenTreeNode 的友元函数 int x; if ( t1 == NULL && t2 == NULL ) return 1; //表 t1 与表 t2 都是空树, 相等 if ( t1 != NULL && t2 != NULL && t1->utype == t2->utype ) { //两子树都非空且结点类型相同 switch ( t1->utype ) { //比较对应数据 case 0 : x = ( t1->RootData == t2->RootData ) ? 1 : 0; //根数据结点 break; case 1 : x = ( t1->ChildData == t2->ChildData ) ? 1 : 0; //子女数据结点 break;
第6章树与森林 case 2: x= Equal(tl->firstChild, t2-> firstChild )i ∥递归比较其子树 if (x)return Equal( tl->nextsibling, t2->nextSibling )i ∥相等,递归比较同一层的下一个结点;不等,不再递归比较 eturn 0: (4) operator<<()用广义表的形式输出一棵树 ostream& operator << ostream& out, Gen Tree& t)t ∥反元函数,将树t输出到输出流对象oute t traverse( out, t first ) return out: void Gen Tree :: traverse( ostream& out, Gen TreeNode*ptr) 私有函数,广义树的遍历算法 if ptr I=NULL ) if ptr->utype ==0)out < ptr-> RootData <<' ∥根数据结点 else if ptr->utype=1)i ∥子女数据结点 Data; I= NULL)out ∥子树结点 traverse( ptr->firstChild ) m向子树方向搜索 if( ptr->nextSibling I=NULL )out < traverse( ptr->nextSibling ) ∥向同一层下一兄弟搜索 5)析构函数清除一棵用广义表表示的树 GenTree :EntRee(i ∥用广义表表示的树的析构函数,假定frst≠NULL Remove( first )i void GenTree Remove( Gen ptr )t Gen TreeNode*p; while( ptr I=NULL)( p= ptr->nextSibling; if( p->utype ==2)Remove( p->firstChild ) ∥在子树中删除
第 6 章 树与森林 69 case 2 : x = Equal ( t1->firstChild, t2->firstChild ); //递归比较其子树 } if ( x ) return Equal ( t1->nextSibling, t2->nextSibling ); //相等, 递归比较同一层的下一个结点; 不等, 不再递归比较 } return 0; } (4) operator << ( ) 用广义表的形式输出一棵树 ostream& operator << ( ostream& out, GenTree& t ) { //友元函数, 将树 t 输出到输出流对象 out。 t.traverse ( out, t.first ); return out; } void GenTree :: traverse ( ostream& out, GenTreeNode * ptr ) { //私有函数, 广义树的遍历算法 if ( ptr != NULL ) { if ( ptr->utype == 0 ) out << ptr->RootData << ‘(’; //根数据结点 else if ( ptr->utype == 1 ) { //子女数据结点 out << ptr->ChildData; if ( ptr->nextSibling != NULL ) out << ‘,’; } else { //子树结点 traverse ( ptr->firstChild ); //向子树方向搜索 if ( ptr->nextSibling != NULL ) out << ‘,’; } traverse ( ptr->nextSibling ); //向同一层下一兄弟搜索 } else out << ‘)’; } (5) 析构函数 清除一棵用广义表表示的树 GenTree :: ~ GenTree ( ) { //用广义表表示的树的析构函数, 假定 first ≠ NULL Remove ( first ); } void GenTree :: Remove ( GenTreeNode *ptr ) { GenTreeNode * p; while ( ptr != NULL ) { p = ptr->nextSibling; if ( p->utype == 2 ) Remove ( p->firstChild ); //在子树中删除
第6章树与森林 ptr->nextSibling=p->nextSibling: delete(p); ∥释放结点 6-2列出右图所示二叉树的叶结点、分支结点和每个结点的层次 【解答】 二又树的叶结点有⑥、⑧、⑨。分支结点有①、②、③、④、⑤、⑦。④ 结点①的层次为0:结点②、③的层次为1:结点④、⑤、⑥的层次 为2;结点⑦、⑧的层次为3:结点⑨的层次为4 6-3使用(1)顺序表示和(2)二叉链表表示法,分别画出右图所示二叉树的存储表示。 【解答】 01234 6789 ②|③ ⑤⑥⑦ 园內 囚人 顺序表示 回A 叉链表表示 6-4用嵌套类写出用链表表示的二叉树的类声明 【解答】 #include <iostream. h> template <class Type> class Binary Tree t private: template <class Type> class Bin TreeNode i BinTreeNode<Type> *leftChild, *rightChild ata: Type dat Type Revalue; BinTreeNode<Type>*root; BinTreeNode<Type>*Parent( BinTreeNode<Type>*start, BinTreeNode<Type>*current ) int Insert( BinTreeNode<Type>*current, const Type& item )i t Find( BinTreeNode<Type>*current, const Type& item )const; void destroy( BinTreeNode<Type> *current )i void Traverse( BinTreeNode<Type> *current, ostream& out)const public: Binary Tree () root( NULL) Binary Tree( Type value): RefValue( value ) root( NULL Binary Tree(i destroy(root);) BinTreeNode ( leftChild( NUlL ) right Child(NULL)0 BinTreeNode( Type item ) data( item ) left Child( NUll ) rightChild NULL)0
第 6 章 树与森林 70 ptr->nextSibling = p->nextSibling; delete ( p ); //释放结点 p } } 6-2 列出右图所示二叉树的叶结点、分支结点和每个结点的层次。 【解答】 二叉树的叶结点有⑥、⑧、⑨。分支结点有①、②、③、④、⑤、⑦。 结点①的层次为 0;结点②、③的层次为 1;结点④、⑤、⑥的层次 为 2;结点⑦、⑧的层次为 3;结点⑨的层次为 4。 6-3 使用 (1) 顺序表示和 (2) 二叉链表表示法,分别画出右图所示二叉树的存储表示。 【解答】 6-4 用嵌套类写出用链表表示的二叉树的类声明。 【解答】 #include <iostream.h> template <class Type> class BinaryTree { private: template <class Type> class BinTreeNode { public: BinTreeNode<Type> *leftChild, *rightChild; Type data; } Type RefValue; BinTreeNode<Type> * root; BinTreeNode<Type> *Parent ( BinTreeNode<Type> *start, BinTreeNode<Type> *current ); int Insert ( BinTreeNode<Type> *current, const Type& item ); int Find ( BinTreeNode<Type> *current, const Type& item ) const; void destroy ( BinTreeNode<Type> *current ); void Traverse ( BinTreeNode<Type> *current, ostream& out ) const; public: BinaryTree ( ) : root ( NULL) { } BinaryTree ( Type value ) : RefValue ( value ), root ( NULL ){ } ~BinaryTree ( ) { destroy (root); } BinTreeNode ( ) : leftChild ( NULL), rightChild ( NULL) { } BinTreeNode ( Type item ) : data ( item ), leftChild ( NULL ), rightChild ( NULL ) {} ① ② ③ ④ ⑤ ⑥ ⑦ ⑨ ⑧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ ∧ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ 顺序表示 二叉链表表示