二叉排序树

二叉排序树

1.二叉排序树定义

二叉排序树(Binary Sort Tree)或者是一棵空树;或者是具有下列性质的二叉树:

(1)若左子树不空,则左子树上所有结点的值均小于根结点的值;若右子树不空,则右子树上所有结点的值均大于根结点的值。

(2)左右子树也都是二叉排序树,如图6-2所示。

2.二叉排序树的查找过程

由其定义可见,二叉排序树的查找过程为:

(1)若查找树为空,查找失败。

(2)查找树非空,将给定值key 与查找树的根结点关键码比较。

(3)若相等,查找成功,结束查找过程,否则:

① 当给值key 小于根结点关键码,查找将在以左孩子为根的子树上继续进行,转(1)。 ② 当给值key 大于根结点关键码,查找将在以右孩子为根的子树上继续进行,转(1)。

3.二叉排序树插入操作和构造一棵二叉排序树

向二叉排序树中插入一个结点的过程:设待插入结点的关键码为key ,为将其插入,先要在二叉排序树中进行查找,若查找成功,按二叉排序树定义,该插入结点已存在,不用插入;查找不成功时,则插入之。因此,新插入结点一定是作为叶子结点添加上去的。构造一棵二叉排序树则是逐个插入结点的过程。对于关键码序列为:{63,90,70,55,67,42,98,83,10,45,58},则构造一棵二叉排序树的过程如图6-3所示。

4.二叉排序树删除操作

从二叉排序树中删除一个结点之后,要求其仍能保持二叉排序树的特性。

设待删结点为*p(p 为指向待删结点的指针),其双亲结点为*f,删除可以分三种情况,如图6-4所示。

(1)*p结点为叶结点,由于删去叶结点后不影响整棵树的特性,所以,只需将被删结点的双亲结点相应指针域改为空指针,如图6-4(a )所示。

(2)*p结点只有右子树或只有左子树,此时,只需将或替换*f结点的*p子树即可,如图6-4(b )、(c )所示。

(3)*p结点既有左子树又有右子树,可按中序遍历保持有序地进行调整,如图6-4(d )、(e )所示。

设删除*p结点前,中序遍历序列为:

① P为F 的左子女时有:…,Pi 子树,P ,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,P1,S1子树,F ,…。

② P 为F 的右子女时有:…,F ,Pi 子树,P ,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,P1,S1子树,…。

则删除*p结点后,中序遍历序列应为:

① P 为F 的左子女时有:…,Pi 子树,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,P1,S1子树,F ,…。

② P为F 的右子女时有:…,F ,Pi 子树,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,

P1,S1子树,…。

有两种调整方法:

① 直接令Pi 为*f相应的子树,以Pr 为Pi 中序遍历的最后一个结点Pk 的右子树。 ② 令*p结点的直接前驱Pr 或直接后继(对Pi 子树中序遍历的最后一个结点Pk )替换*p结点,再按(2)的方法删去Pr 或Pk 。

【算法分析】

对给定序列建立二叉排序树,若左右子树均匀分布,则其查找过程类似于有序表的折半查找。但若给定序列原本有序,则建立的二叉排序树就蜕化为单链表,其查找效率和顺序查找一样。因此,对均匀的二叉排序树进行插入或删除结点后,应进行调整,使其依然保持均匀。

3.2.5 二叉排序树

在这一部分我们要掌握的是二叉排序树的概念、查找、插入和删除操作。在以下的知识点中,二叉排序树的删除相对于其他知识点要复杂一些,但只要掌握了规则,题目还是很容易解决的。下面我们先分别给出各部分的介绍及算法实现,再对一些典型题目进行解析。

(1)二叉排序树

二叉排序树是一种常用的动态查找表,下面首先给出它的非递归形式。

二叉排序树是一棵二叉树,它或者为空,或者具有如下性质:

①任一非终端结点若有左孩子,则该结点的关键字值大于其左孩子结点的关键字值。 ②任一非终端结点若有右孩子,则该结点的关键字值小于其右孩子结点的关键字值。 二叉排序树也可以用递归的形式定义,即二叉排序树是一棵树,它或者为空,或者具有如下性质:

①若它的左子树非空,则其左子树所有结点的关键字值均小于其根结点的关键字值。 ②若它的右子树非空,则其右子树所有结点的关键字值均大于其根结点的关键字值。 ③它的左右子树都是二叉排序树。

例如,由关键字值序列(62,15,68,46,65,12,57,79,35)构成的一棵二叉排序树如图3-38所示。

如果对上述二叉排序树进行中序遍历可以得到一个关键字有序序列(12,15,35,46,57,62,65,68,79),这是二叉排序树的一个重要特征,也正是由此将其称为"

二叉排序树" 。

(2)二叉排序树的查找

二叉排序树的结构定义中可看到:一棵非空二叉排序树中根结点的关键字值大于其左子树上所有结点的关键字值,而小于其右子树上所有结点的关键字值。因此在二叉排序树中查找一个关键字值为k 的结点的基本思想是:用给定值k 与根结点关键字值比较,如果k 小于根结点的值,则要找的结点只可能在左子树中,所以继续在左子树中查找,否则将继续在右子树中查找,依此方法,查找下去,至直查找成功或查找失败为止。二叉排序树查找的过程描述如下:

①若二叉树为空树,则查找失败;

②将给定值k 与根结点的关键字值比较,若相等,则查找成功;

③若根结点的关键字值小于给定值k ,则在左子树中继续搜索;

④否则,在右子树中继续查找。

假定二叉排序树的链式存储结构的类型定义如下:

1

2

3

4

5

6 typedef struct linklist{ keytype key; anytype otherelem; struct linklist*lchild; struct linklist*rchild; }Bin_Sort_Tree_Linklist,*Bin_Sort_Tree;

二叉排序树查找过程的描述是一个递归过程,若用链式存储结构存储,其查找操作的递归算法如下所示:

7

8

9 Bin_Sort_Tree_Linklist*bt_search(Bin_Sort_Tree bt,keytype k) {∥在根指针为bt 的二叉排序树上查找一个关键字值为k 的结点, ∥若查找成功返回指向该结点的指针,否则返回空指针

10 if (bt=NULL)‖(bt->key==k)return bt;

11 else if (kkey)return bt_search(bt->lchild,k);

12 ∥在左子树中搜索

13 else return bt_search(bt->rchild,k);//在右子树中搜索

14 }

这个过程也可以用非递归算法实现,算法描述如下:

15 Bin_Sort_Tree_Linklist*bt_search1(Bin_Sort_Tree bt,keytype k)

16 {

17 p=bt;∥指针p 指向根结点,搜索从根结点开始

18 while (p!=NULL && p->key!=k)

19 {

20 if (k

key)p=p->lchild;

21 else p=p->rchild;

22 }

23 return (p);

24 } 3)二叉排序树的插入

在一棵二叉排序树中插入一个结点可以用一个递归的过程实现。若二叉排序树为空,则新结点作为二叉排序树的根结点。否则,若给定结点的关键字值小于根结点关键字值,则插入在左子树上;若给定结点的关键字值大于根结点的值,则插入在右子树上。

下面是二叉排序树插入操作的递归算法。

1

2

3

4

5

6 void bt_insert1(Bin_Sort_Tree*bt,Bin_Sort_Tree_Linklist*pn) {∥在以bt 为根的二叉排序树上插入一个由指针pn 指向的新的结点 if (*bt=NULL)*bt=pn; else if (*bt->key>pn->key)bt_insert 1(&(*bt->lchild),pn); else if (*bt->keykey)bt_insert1(&(*bt->rchild),pn); }

这个算法也可以用非递归的形式实现,如下所示:

7

8

9 void bt_insert 2(Bin_Sort_Tree*bt, Bin_Sort_Tree_Linklist*pn) {

10 p=bt;

11 while (p!=NULL && p->key!=pn->key)

12 {

13 q=p;

14 if (p->key>pn->key)p=p->lchild;

15 else p=p->rchild;

16 }

17 if (p=NULL){

18 if (q->key>pn->key)q->lchild=pn;

19 else q->rchild=pn->key;

20 }

21 } 利用二叉排序树的插入算法,可以很容易地实现创建二叉排序树的操作,其基本思想为:由一棵空二叉树开始,经过一系列的插入操作生成一棵二叉排序树。

例如,由结点关键字序列(62,15,68,46,65,12,57,79,35)构造二叉排序树的过程为:从空二叉树开始,依次将每个结点插入到二叉排序树中,在插入每个结点时都是从根结点开始搜索插入位置,找到插入位置后,将新结点作为叶子结点插入,经过9次的插入操作,建成由这9个结点组成的二叉排序树。

创建二叉排序树的算法如下:

22 Bin_Sort_Tree_Linklist*bt_bulid(Bin_Sort_Tree a,int n)

23 {∥在数组a 的a [1]~a[n ]

24 单元中存放着将要构成二叉排序树的n 个结点内容

25 bt=NULL;

26 for (i=1;i

27 {

28 p=(Bin_Sort_Tree_Linklist*)malloc

29 (sizeof (Bin_Sort_Tree_Linklist));

30 p->key=a[i ].key;

31 p->otherelem=a[i ].otherelem;;

32 p->lchile=NULL;

33 p->rchile=NULL;

34 bt_insert1(&bt,p);

35 }

36 return (bt);

37 }

(4)二叉排序树的删除

下面分四种情况讨论一下如何确保从二叉树中删除一个结点后,不会影响二叉排序树的性质:

①若要删除的结点p 为叶子结点,可以直接进行删除。

②若要删除结点p 有右子树,但无左子树,可用其右子树的根结点取代要删除结点的位置。

③若要删除结点p 有左子树,但无右子树,可用其左子树的根结点取代要删除结点的位置,与步骤②类似。

④若要删除结点p 的左右子树均非空,则在其左子树中找到最右结点r 来代替被删的结点(即将r 所指结点的右指针置成p 所指结点的右子树的根,然后用p 所指结点的左子树的根结点代替被删的p 所指结点) 。

可以按下述方法来查找到左子树中的最大元素值的结点:首先移动到子树的根,然后沿着各节点的右孩子指针移动,直到右孩子指针为0为止,此时找到的结点即为左子树中的最大元素值的结点。

下面是二叉排序树删除算法的实现:

38 void delnode(btree*b,int x)

39 {

40 btree*p,*q,*r,*t;

41 p=b;

42 q=NULL;

43 while (p!=NULL && p->data!=x)

44 if (x

data)

45 {

46 q=p;

47 p=p->left;

48 }

49 else

50 {

51 q=p;

52 p=p->right;

53 }

54 if (p==NULL)pintf("未发现数据域为%d的结点\\n",x);

55 else if (p->left==NULL)

56 {

57 if (q==NULL)t=p->right;

58 else if (q->left==p)q->left=p->right;

59 else q->right=p->right;

60 }

61 else /*被删结点有左子树*/

62 /*查找被删结点左子树中的最右结点,即刚好小于x 的结点*/

63 {

64 r=p->left;

65 while (r-right!=NULL)

66 r=r-right;

67 /*被删结点的右子树作为r 的右子树*/

68 r-right=p->right;

69 /*被删结点的左子树根代替被删结点*/

70 if (q==NULL)t=p->left; /*被删结点是根结点*/

71 else if (q->left==p)q->left=p->left;

72 else q->right=p->left;

73 }

74 } 【习题及解析】

【例3-34】 二叉树为二叉排序树的()条件是其任一结点的值均大于其左孩子的值,小于其右孩子的值。

A. 充分不必要 B.必要不充分 C.充分必要 D.既不充分也不必要

【分析】 本题考查二叉排序树的定义。由二叉排序树的定义可知,在二叉排序树中,任一非终端结点的关键字的值大于其左子树中所有结点的关键字的值(当然也大于其左孩子的值),小于右子树中所有结点的关键字的值(当然也小于其右孩子的值)。分析至此,我们知道该题的选项中至少应该包括必要条件,故A 、D 可以排除。下面我们看是否充分,也即满足题干要求的二叉树是否一定是二叉排序树。我们可以用反证法,举出一个例子证明满足题干要求的二叉树不一定是二叉排序树。图3-39所示的二叉树满足题干的要求,54,46,但显然这不是一棵二叉排序树,其中序遍历序列为4,6,5,并非一个单增或单减的序列。故正确答案中不应包含充分条件,排除C 。所以本题应该选B 。

【解答】 B。

【例3-35】 设数据集合d={1,12,5,8,3,10,7,13,9},试完成下列各题:

①依次取d 中各数据,构造一棵二叉排序树bt 。

②如何依据此二叉树bt 得到d 的一个有序序列。

③画出在二叉树bt 中删除

"12" 后的树结构。

【分析】 本题考查的是有关二叉排序树的基本概念。其中①考查二叉排序树的生成和插入;②考查如何根据二叉排序树得到有序序列,即二叉排序树中序遍历;

③考查二叉排序树的删除,且是删除结点中被删结点左右子树都非空的情形。

依照前面介绍的二叉排序树的删除结点的规则,删除结点12时应先将其左子树中最右结点(结点10) 的右指针指向其右子树的根结点

(结点13) ,即将结点10的右指针指向结点13,然后用其左子树的根(结点5) 来替代被删除的结点12,即将结点1的右指针指向结点5。

【解答】 ①构造二叉排序树的过程如图3-40所示。

②d的有序序列为bt 的中序遍历次序,即1,3,5,7,8,9,10,12,13。

③删除"12" 后树结构如图3-41所示。

【例3-36】 已知一棵二叉排序树,其结构如图3-42a 所示。画出依次删除关键字为a1=13,a2=12,a3=4,a4=8的各个结点后,该二叉排序树的结构。

【分析】 本题主要考查知识点二叉排序树的删除。其中a1=13为叶结点,可以直接删除,即将其父结点指向该结点的指针置为空。a2=12和a4=8结点为左右子树均非空的结点,删除的时候需将其左子树中的最右结点替代被删除的结点。a3=4结点为左子树为空,右子树非空的结点,删除时需用右子树的根结点替代被删除的结点。

【解答】 依次删除关键字为a1=13,a2=12,a3=4,a4=8的各个结点后,该二叉排序树的变化情况如图3-42所示。

二叉排序树的插入和建立(通过插入实现)算法不考虑树的平衡问题,因此有可能产生高度不平衡的二叉排序树,极端情况下将退化成一个单链表,如图3-43所示,失去了二叉排序树的优点。因此在二叉排序树的插入过程中必须调整树的结构,使之平衡化。为此我们引入平衡二叉树(AVL树) 的概念。

二叉排序树

1.二叉排序树定义

二叉排序树(Binary Sort Tree)或者是一棵空树;或者是具有下列性质的二叉树:

(1)若左子树不空,则左子树上所有结点的值均小于根结点的值;若右子树不空,则右子树上所有结点的值均大于根结点的值。

(2)左右子树也都是二叉排序树,如图6-2所示。

2.二叉排序树的查找过程

由其定义可见,二叉排序树的查找过程为:

(1)若查找树为空,查找失败。

(2)查找树非空,将给定值key 与查找树的根结点关键码比较。

(3)若相等,查找成功,结束查找过程,否则:

① 当给值key 小于根结点关键码,查找将在以左孩子为根的子树上继续进行,转(1)。 ② 当给值key 大于根结点关键码,查找将在以右孩子为根的子树上继续进行,转(1)。

3.二叉排序树插入操作和构造一棵二叉排序树

向二叉排序树中插入一个结点的过程:设待插入结点的关键码为key ,为将其插入,先要在二叉排序树中进行查找,若查找成功,按二叉排序树定义,该插入结点已存在,不用插入;查找不成功时,则插入之。因此,新插入结点一定是作为叶子结点添加上去的。构造一棵二叉排序树则是逐个插入结点的过程。对于关键码序列为:{63,90,70,55,67,42,98,83,10,45,58},则构造一棵二叉排序树的过程如图6-3所示。

4.二叉排序树删除操作

从二叉排序树中删除一个结点之后,要求其仍能保持二叉排序树的特性。

设待删结点为*p(p 为指向待删结点的指针),其双亲结点为*f,删除可以分三种情况,如图6-4所示。

(1)*p结点为叶结点,由于删去叶结点后不影响整棵树的特性,所以,只需将被删结点的双亲结点相应指针域改为空指针,如图6-4(a )所示。

(2)*p结点只有右子树或只有左子树,此时,只需将或替换*f结点的*p子树即可,如图6-4(b )、(c )所示。

(3)*p结点既有左子树又有右子树,可按中序遍历保持有序地进行调整,如图6-4(d )、(e )所示。

设删除*p结点前,中序遍历序列为:

① P为F 的左子女时有:…,Pi 子树,P ,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,P1,S1子树,F ,…。

② P 为F 的右子女时有:…,F ,Pi 子树,P ,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,P1,S1子树,…。

则删除*p结点后,中序遍历序列应为:

① P 为F 的左子女时有:…,Pi 子树,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,P1,S1子树,F ,…。

② P为F 的右子女时有:…,F ,Pi 子树,Pj ,S 子树,Pk ,Sk 子树,…,P2,S2子树,

P1,S1子树,…。

有两种调整方法:

① 直接令Pi 为*f相应的子树,以Pr 为Pi 中序遍历的最后一个结点Pk 的右子树。 ② 令*p结点的直接前驱Pr 或直接后继(对Pi 子树中序遍历的最后一个结点Pk )替换*p结点,再按(2)的方法删去Pr 或Pk 。

【算法分析】

对给定序列建立二叉排序树,若左右子树均匀分布,则其查找过程类似于有序表的折半查找。但若给定序列原本有序,则建立的二叉排序树就蜕化为单链表,其查找效率和顺序查找一样。因此,对均匀的二叉排序树进行插入或删除结点后,应进行调整,使其依然保持均匀。

3.2.5 二叉排序树

在这一部分我们要掌握的是二叉排序树的概念、查找、插入和删除操作。在以下的知识点中,二叉排序树的删除相对于其他知识点要复杂一些,但只要掌握了规则,题目还是很容易解决的。下面我们先分别给出各部分的介绍及算法实现,再对一些典型题目进行解析。

(1)二叉排序树

二叉排序树是一种常用的动态查找表,下面首先给出它的非递归形式。

二叉排序树是一棵二叉树,它或者为空,或者具有如下性质:

①任一非终端结点若有左孩子,则该结点的关键字值大于其左孩子结点的关键字值。 ②任一非终端结点若有右孩子,则该结点的关键字值小于其右孩子结点的关键字值。 二叉排序树也可以用递归的形式定义,即二叉排序树是一棵树,它或者为空,或者具有如下性质:

①若它的左子树非空,则其左子树所有结点的关键字值均小于其根结点的关键字值。 ②若它的右子树非空,则其右子树所有结点的关键字值均大于其根结点的关键字值。 ③它的左右子树都是二叉排序树。

例如,由关键字值序列(62,15,68,46,65,12,57,79,35)构成的一棵二叉排序树如图3-38所示。

如果对上述二叉排序树进行中序遍历可以得到一个关键字有序序列(12,15,35,46,57,62,65,68,79),这是二叉排序树的一个重要特征,也正是由此将其称为"

二叉排序树" 。

(2)二叉排序树的查找

二叉排序树的结构定义中可看到:一棵非空二叉排序树中根结点的关键字值大于其左子树上所有结点的关键字值,而小于其右子树上所有结点的关键字值。因此在二叉排序树中查找一个关键字值为k 的结点的基本思想是:用给定值k 与根结点关键字值比较,如果k 小于根结点的值,则要找的结点只可能在左子树中,所以继续在左子树中查找,否则将继续在右子树中查找,依此方法,查找下去,至直查找成功或查找失败为止。二叉排序树查找的过程描述如下:

①若二叉树为空树,则查找失败;

②将给定值k 与根结点的关键字值比较,若相等,则查找成功;

③若根结点的关键字值小于给定值k ,则在左子树中继续搜索;

④否则,在右子树中继续查找。

假定二叉排序树的链式存储结构的类型定义如下:

1

2

3

4

5

6 typedef struct linklist{ keytype key; anytype otherelem; struct linklist*lchild; struct linklist*rchild; }Bin_Sort_Tree_Linklist,*Bin_Sort_Tree;

二叉排序树查找过程的描述是一个递归过程,若用链式存储结构存储,其查找操作的递归算法如下所示:

7

8

9 Bin_Sort_Tree_Linklist*bt_search(Bin_Sort_Tree bt,keytype k) {∥在根指针为bt 的二叉排序树上查找一个关键字值为k 的结点, ∥若查找成功返回指向该结点的指针,否则返回空指针

10 if (bt=NULL)‖(bt->key==k)return bt;

11 else if (kkey)return bt_search(bt->lchild,k);

12 ∥在左子树中搜索

13 else return bt_search(bt->rchild,k);//在右子树中搜索

14 }

这个过程也可以用非递归算法实现,算法描述如下:

15 Bin_Sort_Tree_Linklist*bt_search1(Bin_Sort_Tree bt,keytype k)

16 {

17 p=bt;∥指针p 指向根结点,搜索从根结点开始

18 while (p!=NULL && p->key!=k)

19 {

20 if (k

key)p=p->lchild;

21 else p=p->rchild;

22 }

23 return (p);

24 } 3)二叉排序树的插入

在一棵二叉排序树中插入一个结点可以用一个递归的过程实现。若二叉排序树为空,则新结点作为二叉排序树的根结点。否则,若给定结点的关键字值小于根结点关键字值,则插入在左子树上;若给定结点的关键字值大于根结点的值,则插入在右子树上。

下面是二叉排序树插入操作的递归算法。

1

2

3

4

5

6 void bt_insert1(Bin_Sort_Tree*bt,Bin_Sort_Tree_Linklist*pn) {∥在以bt 为根的二叉排序树上插入一个由指针pn 指向的新的结点 if (*bt=NULL)*bt=pn; else if (*bt->key>pn->key)bt_insert 1(&(*bt->lchild),pn); else if (*bt->keykey)bt_insert1(&(*bt->rchild),pn); }

这个算法也可以用非递归的形式实现,如下所示:

7

8

9 void bt_insert 2(Bin_Sort_Tree*bt, Bin_Sort_Tree_Linklist*pn) {

10 p=bt;

11 while (p!=NULL && p->key!=pn->key)

12 {

13 q=p;

14 if (p->key>pn->key)p=p->lchild;

15 else p=p->rchild;

16 }

17 if (p=NULL){

18 if (q->key>pn->key)q->lchild=pn;

19 else q->rchild=pn->key;

20 }

21 } 利用二叉排序树的插入算法,可以很容易地实现创建二叉排序树的操作,其基本思想为:由一棵空二叉树开始,经过一系列的插入操作生成一棵二叉排序树。

例如,由结点关键字序列(62,15,68,46,65,12,57,79,35)构造二叉排序树的过程为:从空二叉树开始,依次将每个结点插入到二叉排序树中,在插入每个结点时都是从根结点开始搜索插入位置,找到插入位置后,将新结点作为叶子结点插入,经过9次的插入操作,建成由这9个结点组成的二叉排序树。

创建二叉排序树的算法如下:

22 Bin_Sort_Tree_Linklist*bt_bulid(Bin_Sort_Tree a,int n)

23 {∥在数组a 的a [1]~a[n ]

24 单元中存放着将要构成二叉排序树的n 个结点内容

25 bt=NULL;

26 for (i=1;i

27 {

28 p=(Bin_Sort_Tree_Linklist*)malloc

29 (sizeof (Bin_Sort_Tree_Linklist));

30 p->key=a[i ].key;

31 p->otherelem=a[i ].otherelem;;

32 p->lchile=NULL;

33 p->rchile=NULL;

34 bt_insert1(&bt,p);

35 }

36 return (bt);

37 }

(4)二叉排序树的删除

下面分四种情况讨论一下如何确保从二叉树中删除一个结点后,不会影响二叉排序树的性质:

①若要删除的结点p 为叶子结点,可以直接进行删除。

②若要删除结点p 有右子树,但无左子树,可用其右子树的根结点取代要删除结点的位置。

③若要删除结点p 有左子树,但无右子树,可用其左子树的根结点取代要删除结点的位置,与步骤②类似。

④若要删除结点p 的左右子树均非空,则在其左子树中找到最右结点r 来代替被删的结点(即将r 所指结点的右指针置成p 所指结点的右子树的根,然后用p 所指结点的左子树的根结点代替被删的p 所指结点) 。

可以按下述方法来查找到左子树中的最大元素值的结点:首先移动到子树的根,然后沿着各节点的右孩子指针移动,直到右孩子指针为0为止,此时找到的结点即为左子树中的最大元素值的结点。

下面是二叉排序树删除算法的实现:

38 void delnode(btree*b,int x)

39 {

40 btree*p,*q,*r,*t;

41 p=b;

42 q=NULL;

43 while (p!=NULL && p->data!=x)

44 if (x

data)

45 {

46 q=p;

47 p=p->left;

48 }

49 else

50 {

51 q=p;

52 p=p->right;

53 }

54 if (p==NULL)pintf("未发现数据域为%d的结点\\n",x);

55 else if (p->left==NULL)

56 {

57 if (q==NULL)t=p->right;

58 else if (q->left==p)q->left=p->right;

59 else q->right=p->right;

60 }

61 else /*被删结点有左子树*/

62 /*查找被删结点左子树中的最右结点,即刚好小于x 的结点*/

63 {

64 r=p->left;

65 while (r-right!=NULL)

66 r=r-right;

67 /*被删结点的右子树作为r 的右子树*/

68 r-right=p->right;

69 /*被删结点的左子树根代替被删结点*/

70 if (q==NULL)t=p->left; /*被删结点是根结点*/

71 else if (q->left==p)q->left=p->left;

72 else q->right=p->left;

73 }

74 } 【习题及解析】

【例3-34】 二叉树为二叉排序树的()条件是其任一结点的值均大于其左孩子的值,小于其右孩子的值。

A. 充分不必要 B.必要不充分 C.充分必要 D.既不充分也不必要

【分析】 本题考查二叉排序树的定义。由二叉排序树的定义可知,在二叉排序树中,任一非终端结点的关键字的值大于其左子树中所有结点的关键字的值(当然也大于其左孩子的值),小于右子树中所有结点的关键字的值(当然也小于其右孩子的值)。分析至此,我们知道该题的选项中至少应该包括必要条件,故A 、D 可以排除。下面我们看是否充分,也即满足题干要求的二叉树是否一定是二叉排序树。我们可以用反证法,举出一个例子证明满足题干要求的二叉树不一定是二叉排序树。图3-39所示的二叉树满足题干的要求,54,46,但显然这不是一棵二叉排序树,其中序遍历序列为4,6,5,并非一个单增或单减的序列。故正确答案中不应包含充分条件,排除C 。所以本题应该选B 。

【解答】 B。

【例3-35】 设数据集合d={1,12,5,8,3,10,7,13,9},试完成下列各题:

①依次取d 中各数据,构造一棵二叉排序树bt 。

②如何依据此二叉树bt 得到d 的一个有序序列。

③画出在二叉树bt 中删除

"12" 后的树结构。

【分析】 本题考查的是有关二叉排序树的基本概念。其中①考查二叉排序树的生成和插入;②考查如何根据二叉排序树得到有序序列,即二叉排序树中序遍历;

③考查二叉排序树的删除,且是删除结点中被删结点左右子树都非空的情形。

依照前面介绍的二叉排序树的删除结点的规则,删除结点12时应先将其左子树中最右结点(结点10) 的右指针指向其右子树的根结点

(结点13) ,即将结点10的右指针指向结点13,然后用其左子树的根(结点5) 来替代被删除的结点12,即将结点1的右指针指向结点5。

【解答】 ①构造二叉排序树的过程如图3-40所示。

②d的有序序列为bt 的中序遍历次序,即1,3,5,7,8,9,10,12,13。

③删除"12" 后树结构如图3-41所示。

【例3-36】 已知一棵二叉排序树,其结构如图3-42a 所示。画出依次删除关键字为a1=13,a2=12,a3=4,a4=8的各个结点后,该二叉排序树的结构。

【分析】 本题主要考查知识点二叉排序树的删除。其中a1=13为叶结点,可以直接删除,即将其父结点指向该结点的指针置为空。a2=12和a4=8结点为左右子树均非空的结点,删除的时候需将其左子树中的最右结点替代被删除的结点。a3=4结点为左子树为空,右子树非空的结点,删除时需用右子树的根结点替代被删除的结点。

【解答】 依次删除关键字为a1=13,a2=12,a3=4,a4=8的各个结点后,该二叉排序树的变化情况如图3-42所示。

二叉排序树的插入和建立(通过插入实现)算法不考虑树的平衡问题,因此有可能产生高度不平衡的二叉排序树,极端情况下将退化成一个单链表,如图3-43所示,失去了二叉排序树的优点。因此在二叉排序树的插入过程中必须调整树的结构,使之平衡化。为此我们引入平衡二叉树(AVL树) 的概念。


相关内容

  • 数据结构排序毕业论文
  • 内容摘要 ..................................................................................................................................... 3 关键字 ..... ...

  • 各种排序算法的复杂度排序法
  • 各种排序算法的复杂度 各算法的时间复杂度 平均时间复杂度 插入排序 O(n^2) 冒泡排序 O(n^2) 选择排序 O(n^2) 快速排序 O(n log n) 堆排序 O(n log n) 归并排序 O(n log n) 基数排序 O(n) 希尔排序 O(n^1.25) 1 快速排序(QuickS ...

  • 各种排序的时间复杂度
  • 排序算法 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作. 分类 在计算机科学所使用的排序算法通常被分类为: 计算的复杂度(最差.平均.和最好表现),依据串列(list)的大小(n).一般而言,好的表现是O.(n log n),且坏的行为是Ω(n2).对於一个 ...

  • 第7章 排序 习题参考答案
  • 习题七 参考答案 一.选择题 1.内部排序算法的稳定性是指( D ) . A .该排序算法不允许有相同的关键字记录 B .该排序算法允许有相同的关键字记录 C .平均时间为0(n log n)的排序方法 D .以上都不对 2.下面给出的四种排序算法中,( B ) 是不稳定的排序. A .插入排序 B ...

  • 实验四题目1
  • 数据结构实验报告 实验名称: 实验四--题目一 学生姓名: 唐文旭 班 级:2013211118 班内序号: 09 学 号: 2013210524 日 期: 2015年1月5日 1.实验要求 使用简单数组实现下面各种排序算法,并进行比较. 排序算法: 1.插入排序 2.希尔排序 3.冒泡排序 4.快 ...

  • 数据结构中几种常见的排序算法之比较
  • 几种常见的排序算法之比较 2010-06-2014:04摘要:排序的基本概念以及其算法的种类,介绍几种常见的排序算法的算法:冒泡排的复杂度,然后以表格的形式,清晰直观的表现出它们的复杂度的不同.在研究学关键词: 一.引言 排序算法,是计算机编程中的一个常见问题.在日常的数据处理中,面对纷繁我们也许有 ...

  • 冒泡排序算法的分析与改进 算法设计
  • 冒泡排序算法的分析与改进 孙伟 (安徽中医学院 医药信息工程学院 09医软一班,安徽合肥,230009) 摘 要: 冒泡排序算法有两个优点:1"编程复杂度"很低,很容易写出代码:2. 具有稳定性,这里的稳定性是指原序列中相同元素的相对顺序仍然保持到排序后的序列,但当需要排序的数据 ...

  • 请简述各种排序算法的时间复杂度
  • 1.在各种排序算法中,哪些是稳定的?哪些是不稳定的? 2.请简述各种排序算法的时间复杂度,空间复杂度(最好情况,最差情况,平均情况) 答:1.快速排序.希尔排序.堆排序.直接选择排序不是稳定的排序算法,而基数排序.冒泡排序.直接插入排序.折半插入排序.归并排序是稳定的排序算法. 2.时间复杂度 比较 ...

  • 幼儿园里的排序问题
  • 让幼儿在游戏中学会排序 钢新三幼 王立华 [摘要]排序和分类一样,也是幼儿学数前的一种智力准备活动.一方面,它有助于孩子学习数数,另一方面,由于排序活动需连续地对前后两个物体进行比较,有利于幼儿思维能力的发展. [关键词]排序 游戏 排序的过程是一种复杂的比较思维过程,它能促进幼儿有序性和可逆性思维 ...