1 / 92

第五章 二叉树

第五章 二叉树. 树型结构是一类重要的非线性结构。树型结构是结点之间有分支,并且具有层次关系的结构,它非常类似于自然界中的树。树结构在客观世界国是大量存在的,例如家谱、行政组织机构都可用树形象地表示。树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构;在数据库系统中,可用树来组织信息;在分析算法的行为时,可用树来描述其执行过程。等等。 5 .1 树的定义和基本术语 定义:树 (Tree) 是 n(n>=0) 个结点的有限集 T , T 为空时称为空树,否则它满足如下两个条件: ( 1 )有且仅有一个特定的称为根 (Root) 的结点;.

oberon
Download Presentation

第五章 二叉树

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第五章 二叉树

  2. 树型结构是一类重要的非线性结构。树型结构是结点之间有分支,并且具有层次关系的结构,它非常类似于自然界中的树。树结构在客观世界国是大量存在的,例如家谱、行政组织机构都可用树形象地表示。树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构;在数据库系统中,可用树来组织信息;在分析算法的行为时,可用树来描述其执行过程。等等。树型结构是一类重要的非线性结构。树型结构是结点之间有分支,并且具有层次关系的结构,它非常类似于自然界中的树。树结构在客观世界国是大量存在的,例如家谱、行政组织机构都可用树形象地表示。树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构;在数据库系统中,可用树来组织信息;在分析算法的行为时,可用树来描述其执行过程。等等。 5.1 树的定义和基本术语 定义:树(Tree)是n(n>=0)个结点的有限集T,T为空时称为空树,否则它满足如下两个条件: (1)有且仅有一个特定的称为根(Root)的结点;

  3. (2)其余的结点可分为m(m>=0)个互不相交的子集T1,T2,T3…Tm,其中每个子集又是一棵树,并称其为子树(Subtree)。(2)其余的结点可分为m(m>=0)个互不相交的子集T1,T2,T3…Tm,其中每个子集又是一棵树,并称其为子树(Subtree)。

  4. 5.2 二叉树 二叉树在树结构的应用中起着非常重要的作用,因为对二叉树的许多操作算法简单,而任何树都可以与二叉树 相互转换,这样就解决了树的 存储结构及其运算中存在的复杂性。 5.2.1 二叉树的定义 定义:二叉树是由n(n>=0)个结点的有限集合构成,此集合或者为空集,或者由一个根结点及两棵互不相交的左右子树组成,并且左右子树都是二叉树。 这也是一个递归定义。二叉树可以是空集合,根可以有空的左子树或空的右子树。

  5. 二叉树的相关概念 (1)结点的度。结点所拥有的子树的个数称为该结点的度。 (2)叶结点。度为0的结点称为叶结点,或者称为终端结点。 (3)分枝结点。度不为0的结点称为分支结点,或者称为非终端结点。一棵树的结点除叶结点外,其余的都是分支结点。 (4)左孩子、右孩子、双亲。树中一个结点的子树的根结点称为这个结点的孩子。这个结点称为它孩子结点的双亲。具有同一个双亲的孩子结点互称为兄弟。 (5)路径、路径长度。如果一棵树的一串结点n1,n2,…,nk有如下关系:结点ni是ni+1的父结点(1≤i<k),就把n1,n2,…,nk称为一条由n1至nk的路径。这条路径的长度是k-1。 (6)祖先、子孙。在树中,如果有一条路径从结点M到结点N,那么M就称为N的祖先,而N称为M的子孙。 (7)结点的层数。规定树的根结点的层数为1,其余结点的层数等于它的双亲结点的层数加1。 (8)树的深度。树中所有结点的最大层数称为树的深度。 • (9)树的度。树中各结点度的最大值称为该树的度。

  6. 一、二叉树的定义和特点 1、定义:二叉树是有限个结点的集合,它或者是空,或者是由一个根结点加上两棵分别称为左子树和右子树、互不相交的二叉树组成。 2、特点 ◆每个结点最多只有两个孩子结点,即结点的度不大于2。 ◆子树有左右之别,子树的次序(位置)不能颠倒。 3、基本形态 空 只有根结点 根的右子 树为空 根的左子 树为空 根的左右子 树都不空

  7. 第1层(根) 第2层 第3层 第4层 i-1 1、若层次从1开始,则第i层最多有2个结点 h 2、高度为h的二叉树最多有2 -1个结点 二、二叉树的性质 3、任何一棵二叉树,若叶子结点数为n0,度为2的结点数为n2,则n0 = n2 + 1

  8. 5.2.2 二叉树的性质 二叉树具有下列重要性质: 性质1: 在二叉树的第i层上至多有2i-1个结点(i>=1)。 采用归纳法证明此性质。 当i=1时,只有一个根结点,2i-1=20 =1,命题成立。 现在假定多所有的j,1<=j<i,命题成立,即第j层上至多有2j-2个结点,那么可以证明j=i时命题也成立。由归纳假设可知,第i-1层上至多有2i-2个结点。 由于二叉树每个结点的度最大为2,故在第i层上最大结点数为第i-1层上最大结点数的二倍, 即2×2i-2=2i-1。 命题得到证明。

  9. 性质2:深度为k的二叉树至多有2k-1个结点(k>=1).性质2:深度为k的二叉树至多有2k-1个结点(k>=1). 深度为k的二叉树的最大的结点时为二叉树中每层上的最大结点数之和,由性质1得到每层上的最大结点数,: EkI=1(第i层上的最大结点数)= EkI=12i-1=2k–1 性质3: 对任何一棵二叉树,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。 设二叉树中度为1的结点数为n1,二叉树中总结点数为N,因为二叉树中所有结点均小于或等于2,所以有:N=n0+n1+n2 (5-1) 再看二叉树中的分支数,除根结点外,其余结点都有一个进入分支,设B为二叉树中的分支总数, 则有:N=B+1。

  10. 由于这些分支都是由度为1和2的结点射出的,所有有:由于这些分支都是由度为1和2的结点射出的,所有有: B=n1+2*n2 N=B+1=n1+2×n2+1 (5-2) 由式(5-1)和(5-2)得到: n0+n1+n2=n1+2*n2+1 n0=n2+1 下面介绍两种特殊形态的二叉树:满二叉树和完全二叉树。 一棵深度为k且由2k-1个结点的二叉树称为满二叉树。图5.9就是一棵满二叉树,对结点进行了顺序编号。

  11. 如果深度为k、由n个结点的二叉树中个结点能够与深度为k的顺序编号的满二叉树从1到n标号的结点相对应,如果深度为k、由n个结点的二叉树中个结点能够与深度为k的顺序编号的满二叉树从1到n标号的结点相对应, 1 2 3 4 5 7 6 图5.9 满二叉树

  12. 则称这样的二叉树为完全二叉树,图5.10(b)、则称这样的二叉树为完全二叉树,图5.10(b)、 (c)是2棵非完全二叉树。满二叉树是完全二叉树的 特例。 1 1 1 2 3 2 3 2 3 4 4 7 5 6 5 7 6 (a)完全二叉树 ( c)非完全二叉树 (b)非完全二叉树 图5.10 完全二叉树

  13. 完全二叉树的特点是: (1)所有的叶结点都出现在第k层或k-1层。 (2)对任一结点,如果其右子树的最大层次为1,则其左子树的最大层次为1或l+1。 性质4:具有n个结点的完全二叉树的深度为log2n +1。 符号 x 表示不大于x的最大整数。 假设此二叉树的深度为k,则根据性质2及完全二叉树的定义得到:2k-1-1<n<=2k-1 或 2k-1<=n<2k 取对数得到:k-1<=log2n<k 因为k是整数。所以有:k= log2n +1。 └ ┘ └ ┘ └ ┘

  14. 性质5: 如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第 log2n +1层,每层从左到右),则对任一结点i(1<=i<=n),有: (1)如果i=1,则结点i无双亲,是二叉树的根;如果i>1,则其双亲是结点 i/2 。 (2)如果2i>n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i。 (3)如果2i+1>n,则结点i无右孩子;否则,其右孩子是结点2i+1。 └ ┘ └ ┘

  15. 如图5.11所示为完全二叉树上结点及其 左右孩子结点之间的关系。 [I/2] i I+1 i I+1 2i 2i+1 2i 2i+1 2(I+1) 2i+3 2(I+1) 2i+3 (b)I和i+1结点不在同一层 (a)I和i+1结点在同一层 图5.11 完全二叉树中结点I和i+1

  16. 在此过程中,可以从(2)和(3)推出(1),所以先证明(2)和(3)。在此过程中,可以从(2)和(3)推出(1),所以先证明(2)和(3)。 对于i=1,由完全二叉树的定义,其左孩子是结点2,若2>n,即不存在结点2,此是,结点i无孩子。结点i的由孩子也只能是结点3,若结点3不存在,即3>n,此时结点i无右孩子。 对于i>1,可分为两种情况: (1)设第j(1<=j<=[log2n])层的第一个结点的编号为i,由二叉树的性质2和定义知i=2j-1 结点i的左孩子必定为的j+1层的第一个结点,其编号为2j=2×2j-1=2i。如果2i>n,则无左孩子:

  17. 其右孩子必定为第j+1层的第二个结点,编号为2i+1。若2i+1>n,则无右孩子。其右孩子必定为第j+1层的第二个结点,编号为2i+1。若2i+1>n,则无右孩子。 (2)假设第j(1<=j<=[log2n])层上的某个结点编号为i(2e(j-1)<=i<=2ej-1),且2i+1<n,其左孩子为2i,右孩子为2i+1,则编号为i+1的结点时编号为i的结点的右兄弟或堂兄弟。若它有左孩子,则其编号必定为2i+2=2×(i+1):若它有右孩子,则其编号必定为2i+3=2×(i+1)+1。 当i=1时,就是根,因此无双亲,当i>1时,如果i为左孩子,即2×(i/2)=i,则i/2是i的双亲;如果i为右孩子,i=2p+1,i的双亲应为p,p=(i-1)/2=[i/2]. 证毕。

  18. 二叉树的抽象数据类型 二叉树结点的ADT template <class Elem> class BinNode{ public: virtual Elem& val()=0; virtual void setVal(const Elem&)=0; virtual BinNode* left()=0; virtual BinNode* right()=0; virtual void setLeft(BinNode*)=0; virtual void setLeft(BinNode*)=0 ; virtual bool isLeaf()=0; }

  19. 5.2.3 二叉树的存储结构 1.顺序存储结构 它是用一组连续的存储单元存储二叉树的数据元素。因此,必须把二叉树的所有结点安排成为一个恰当的序列,结点在这个序列中的相互位置能反映出结点之间的逻辑关系,用编号的方法: #define max-tree-size 100 Typedef telemtype sqbitree[max-tree-size]; Sqbitree bt 从树根起,自上层至下层,每层自左至右的给所有结点编号缺点是有可能对存储空间造成极大的浪费,在最坏的情况下,一个深度为H且只有H个结点的右单支树确需要2h-1个结点存储空间。而且,若经常需要插入与删除树中结点时,顺序存储方式不是很好!

  20. 完全二叉树 a c b d e f g h i j k l 1 2 3 4 5 6 7 8 9 10 11 12

  21. 一般二叉树 Ø 表示该处没有元素存在仅仅为了好理解

  22. root leftChild data rightChild root A ∧ F E A C B D B ∧ C ∧ D ∧ E ∧ ∧ F ∧ (2)二叉树的二叉动态链式存储表示 1、结点结构和示例

  23. 二叉树的二叉链表存储表示 template <class Elem> class BinaryTree; template <class Elem> class BinTreeNode { firend class BinaryTree<Elem>; private: Elem data; BinTreeNode* lchild; BinTreeNode* rchild; public: BinTreeNode(){lchild=rchild=NULL;} BinTreeNode(Elem e,BinNodePtr*l=NULL, BinNodePtr*r=NULL) {data=e; lchild=l; rchild=r;} ~BinTreeNode(){}

  24. 二叉树的二叉链表存储表示 Elem val(){return data;} void setVal(const Elem e){data=e;} inline BinTreeNode<Elem>* left(){return lchild;} inline BinTreeNode<Elem>* right(){return rchild;} void setLeft(BinTreeNode<Elem>* left){lchild=left;} void setRight(BinTreeNode<Elem>* right){rchild=right;} bool isLeaf() {return (lchild==NULL)&&(rchild=NULL);} }; 有时也可用数组的下标来模拟指针,即开辟三个一维数组Data ,lchild,rchild 分别存储结点的元素及其左,右指针域;

  25. template <class Elem> class BinaryTree{ private: BinTreeNode <Elem> *root; Elem Refvalue; BinTreeNode <Elem> *Parent (BinTreeNode <Elem> *start, BinTreeNode <Elem> *current); void Traverse (BinTreeNode <Elem> *current, ostream & out) const void destroy (BinTreeNode <Elem> *current); … public: Virtual BinaryTree (Elem value): root (NULL), Refvalue (value){ } Virtual ~BinaryTree( ) {destroy (root);} Virtual bool IsEmpty( ) {return root = = NULL ? true :false;} Virtual BinTreeNode <Elem> *Parent (BinTreeNode <Elem> *current) {return Parent (root, current);} … }

  26. 3、部分成员函数的实现 ◆template <class Elem> void BinaryTree <Elem> :: destroy (BinTreeNode <Elem> *current) { //删除以current为根的子树 if (current != NULL) { ①destroy (current -> leftChild); //删除current的左子树 ②destroy (current -> rightChild); //删除current的右子树 ③delete current; }} //删除current ☆算法中,①、②两递归调用语句可互换,它只关系到左右子树中,哪一个先删,哪一个后删。 ☆③语句不能同①和②语句互换,因为那样将导致一个子树甚至两个子树无法删。

  27. ◆template <class Elem> BinTreeNode <Elem> *BinaryTree :: Parent (BinTreeNode <Elem> *start, BinTreeNode <Elem> *current) { if (start = = NULL | | current = = start) return NULL; if (start -> leftChild = = current | | start -> rightChild = = current) return start; //start是双亲 BinTreeNode <Elem> *p; if (p = Parent (start -> leftChild, current) != NULL) return p; //在左子树找 else return Parent (start -> rightChild, current);} //在右子树找 ◆template <class Elem> void Binary Tree <Elem> :: Traverse (BinTreeNode <Elem> *current, ostream & out) const { //搜索并输出根为current的二叉树 if (current != NULL) { out <<current -> data <<' '; //输出根结点 Traverse (current -> leftChild, out); //搜索并输出左子树 Traverse (current -> rightChild, out);}} //搜索并输出右子树

  28. §5.3 遍历二叉树(Traversal Binary Tree) 一、TBT概述 1、定义 ◆按某种次序,访问所有结点,使每个节点都被访问且尽访问一次的运算叫TBT。 ◆“访问”包括输出结点值,修改值,统计等以不破坏BT的结构为原则。 ◆TBT是对二叉树进行运算的基础性操作。

  29. A B C D E F G H bt I J 一、TBT概述 2、次序(V—根,L—左子树,R—右子树) 先序遍历 中序遍历 后序遍历 先右后左: VRL RVL RLV 先左后右: VLR LVR LRV VLR输出序列:A B D E G HI J C F LVR输出序列:D B G EI H J A C F LRV输出序列:D G I JH E B F C A

  30. + / e f a * b - c d 二、TBT的递归算法 1、中序遍历( inorder traversal) template <class Elem> void BinaryTree <Elem> :: InOrder ( ){ InOrder (root);} //公共函数,调用私有函数完成遍历 template <class Elem> void BinaryTree <Elem> :: InOrder (BinTreeNode <Elem> *current) { if (current != NULL) { //当current = = NULL,则递归终结 InOrder (current -> leftChild); //中序遍历左子树 cout <<current -> data; //访问根结点 InOrder (current -> rightChild); } } //中序遍历右子树 对 a+b*(c-d)-e/f 表达式的语法树,中序遍历得到其中缀表示: a + b * c – d – e / f

  31. + / e f a * b - c d 二、TBT的递归算法 2、先序遍历( preorder Traversal) template <class Elem> void BinaryTree <Elem> :: PreOrder (BinTreeNode <Elem> *current) { //私有函数 if (current != NULL) { cout <<current ->data; PreOrder (current -> leftChild); PreOrder (current -> rightChild);} } 对表达式a+b*(c-d)-e/f 的语法树进行先序遍历得到其前缀表示: – + a * b – c d / e f

  32. + / e f a * b - c d 二、TBT的递归算法 3、后序遍历( postorder traversal) template <class Elem> void BinaryTree <Elem> :: PostOrder (BinTreeNode <Elem> *current) { //私有函数 if (current != NULL) { PostOrder (current -> leftChild); PostOrder (current -> rightChild); cout <<current ->data; } } 对表达式 a+b*(c-d)-e/f 的语法树进行后序遍历得到其后缀表示: a b c d – * + e f / –

  33. 三、TBT的非递归算法 1、递归算法中栈的使用 ◆TBT递归算法的简化 traversal (BinTreeNode <Elem> *current) { if (current != NULL) { traversal (current -> leftChild); traversal (current -> rightChild); } }

  34. current ad1 A ad5 ad2 B ∧ ∧ C ∧ ad3 ∧ D -1 0 1 2 3 stack ad4 ad5 ad1 ad2 ad4 ad3 ∧ E ∧ top 三、TBT的非递归算法 1、递归算法中栈的使用 ◆执行过程中栈的情况和current的变化举例 ☆当左向递归则current的值进栈 ☆若current = = NULL,则出栈,current以栈顶值去执行右向递归 ☆直到栈空(top = -1)和current = = NULL为止

  35. 三、TBT的非递归算法 2、利用栈的前序和中序遍历非递归算法 template <class Elem> void BinaryTree <Elem> :: PreOrder ( ) { stack <BinTreeNode <Elem> *> st; BinTreeNode <Elem> *p = root; do { while (p != NULL) { cout <<p -> data; st. Push (p); p = p ->leftChild;} if ( St. length()!=0) {st. pop (p ); p = p -> rightChild; } } while (p != NULL | | st.length ( )!=0);} ◆算法描述 ☆p不空则p进栈,沿左子树方向传递指针 ☆若p空但栈不空,出栈,p以栈顶值沿右子树方向传递指针 ☆循环下去,直到p和栈都空为止 ◆执行中栈的情况和指针的变化与递归程序相同 ◆对于中序遍历只需调整输出语句位置即可

  36. 3、层次遍历 三、TBT的非递归算法 template <class Elem> void BinaryTree <Elem> :: LevelOrder ( ) { queue <BinTreeNode <Elem> *> qu; BinTreeNode <Elem> *p = root; qu.Enqueue(p); while (qu.DeQueue (p )) { cout << p -> data <<endl; if (p -> leftChild != NULL) qu.EnQueue (p -> leftChild); if (p -> rightChild != NULL) qu.EnQueue (p -> rightChild);}}

  37. root p A ad1 B C ad3 ad2 D E F ad4 ad5 ad6 0 1 2 3 4 5 6 7 f r 3、层次遍历 ad1 ad2 ad3 ad4 ad5 ad6 ◆层次遍历是指直上至下,从左到右访问结点 ◆只有采用队列来存放结点地址才能实现层次遍历

  38. §5.4 线索化二叉树 一、概述 1、问题提出 ◆遍历是二叉树最基本的运算,为避免再次遍历的麻烦,可将首次遍历得到的信息存于一个线性结构中。 ◆具有n个结点的链式存贮的二叉树中,有n+1个链域是空的,能否充分利用这些空的链域来存放结点的前趋指针和后继指针呢?

  39. leftChild leftThread data rightThread rightChild 0 0 指向右孩子 指向左孩子 1 指向前驱 1 指向后继 一、概述 2、实现策略 ◆结点中增加两个标记域 ◆两标记域取值范围是0,1。各只占一个二进制位,只增加很少的额外空间开销。

  40. 3、线索二叉树 root root E 0 E 0 B F NULL NULL 1 F 0 A D G 0 B 0  0 D 1 1 G 1  1 A 1 C 1 C 1 ◆指向前驱和指向后继的指针叫做线索(Thread)。 ◆具有线索的二叉树叫做线索二叉树。 ◆因遍历的次序有三种,所以线索二叉树也分前、中、后序这三种。 ◆中序遍历线索二叉树结构示例 ◆若根的左子树不空,侧其最左下角的结点为中序遍历的第一个结点,否则根为中序遍历的第一个结点。 ◆若根的右子树不空,侧其最右下角的结点为中序遍历的最后一个结点,否则根为中序遍历的最后一个结点。

  41. 二、中序线索二叉树的类定义 1、类声明 template <class Elem> class ThreadTree; template <class Elem> class ThreadNode { friend class ThreadTree <Elem>; private: int leftThread, rightThread; ThreadNode <Elem> * leftChild, rightChild; Elem data; public: ThreadNode (const Elem item) : data (item), leftChild (NULL), rightChild (NULL), leftThread (0), rightThread (0) { } Elem getData ( ) const {return data;}}

  42. template <class Elem> class ThreadTree { private : ThreadNode <Elem> *root; InThread (ThreadNode<Elem> *current, ThreadNode<Elem> *pre); public : ThreadTree( ) : root(NULL) { }; ThreadNode<Elem> *First(ThreadNode<Elem> *current); ThreadNode<Elem> *Last(ThreadNode<Elem> * current); ThreadNode<Elem> *Next(ThreadNode<Elem> *current); ThreadNode<Elem> *Prior(ThreadNode<Elem> *current); void Inorder( ); //从第一个结点开始沿着后继方向遍历二叉树 ……}

  43. 2、成员函数的实现 current p next template <class Elem> ThreadNode<Elem> * ThreadTree<Elem>:: First(ThreadNode<Elem> * current) { //返回以current为根指针的线索二叉树中序序列下的第一个结点 ThreadNode <Elem> * p = current; while(p -> leftThread = = 0) p = p -> leftChild; //找最左下角的结点 return p;} template <class Elem> ThreadNode<Elem> * ThreadTree <Elem>:: Next (ThreadNode <Elem> * current) { ThreadNode <Elem> *p = current -> rightChild; if (current -> rightThread = = 0) return first (p); else return p;}

  44. current p prior template <class Elem> ThreadNode<Elem> * ThreadTree<Elem>:: last(ThreadNode<Elem> * current) { ThreadNode <Elem> * p = current; while(p -> rightThread = = 0) p = p -> rightChild; //找右下角结点 return p;} template <class Elem> ThreadNode<Elem> * ThreadTree <Elem>:: Prior (ThreadNode <Elem> * current) { ThreadNode <Elem> *p = current -> leftChild; if (current -> leftThread = = 0) return last (p); else return p;}

  45. template <class Elem> void ThreadTree<Elem> :: InOrder ( ) { ThreadNode <Elem> * p;//在线索二叉树中进行中序遍历 for (p = first(root); p != NULL; p = Next(p)) cout << p -> data << endl; } template <class Elem> void ThreadTree<Elem> :: InThread (ThreadNode<Elem> * current, ThreadNode<Elem> * pre) { //通过中序遍历对以current为根的二叉树进行线索化 if (current != NULL) { InThread (current -> leftChild, pre); //左子树线索化 if (current -> leftChild = = NULL) {current -> leftChild = pre; current -> leftThread = 1;} //建立当前结点的前驱线索 if (pre != NULL && pre -> rightChild = = NULL) {pre -> rightChild = current; pre -> rightThread = 1;} //建前驱的后继线索 pre = current; InThread (current -> rightChild, pre);}} //右子树线索化

  46. 53 17 78 09 45 65 87 23 81 94 5.5 二叉搜索树(Binary Search Tree) 一、基本概念 1、定义——或是空、或是具有下列性质的二叉树: 每个结点有一个作为搜索依据的关键码(Key)。 左子树上所有关键码小于根结点关键码。 右子树上所有关键码大于根结点关键码。 2、举例 中序遍历结果: 09,17,23,45,53,65,78,81,87,94 显然是有序的,故又称二叉排序树。

  47. 一、基本概念 3、BST是基于二叉树的动态搜索结构,其删除和插入结点可能要改变树的结构。 4、BST类定义特点 类定义基于二叉链存贮表示 与一般二叉树类定义十分相似 可以继承一般二叉树类的定义 基本运算Find, Insert 和 Remove 都用递归实现所以在类定义中包括私有和公用两种性质的声明

  48. 二叉查找树的C++类说明 template <class Key, class Elem, class KEComp, class EEComp > class BST: public Dictionary<Key, Elem, KEComp, EEComp>{ private: BinTreeNode<Elem> * root; Elem RefValue int nodecount; void clearhelp(BinTreeNode<Elem>*); BinTreeNode<Elem>* inserthelp(BinTreeNode<Elem>*,const Elem&>); BinTreeNode<Elem>* deletemin(BinTreeNode<Elem>*,BinTreeNode<Elem>*& ) ; BinTreeNode<ELem>* removehelp(BinTreeNode<Elem>*, const Key&, BinTreeNode<Elem>*&); bool findhelp(BinTreeNode<Elem>*, int) const; void printhelp(BinTreeNode<Elem>* ,int) const;

  49. public: BST( ) {root=NULL; nodecount=0;} ~ BST(){clearhelp(root);} void clear(){clearhelp(root); root=NULL; nodecount=0;} bool insert(const Elem& e) {root=inserthelp(root,e); nodecount++; return true;} bool remove(const Key&K, Elem& e){ BinTreeNode<Elem>*t=NULL; root=removehelp(root, K, t); e=t->val(); nodecount--; delete t; return true;} bool removeAny(Elem& e){ if(root==NULL) return false; root=deletemin(root, t); e=t->val(); delete t; nodecount--; return true;}

  50. bool find(counst Key& K, Elem& e) const {return findhelp(root, K,e);} int size(){return nodecount;} void print()const{ if(root==NULL) count<<“The BST is empty.\n”; else printHelp(root, 0); } };

More Related