rambo

二叉搜索树(二)非递归方式

数据结构

查找

在一棵二叉搜索树种查找一个具有给定关键字的结点。输入一个指向树根的指针和一个关键字k,如果这个结点存在,TREE-SEARCH返回一个指向关键字为k的结点的指针;否则返回NULL。

QQ截图20150815222958

伪代码

对于大多数计算机,迭代版本的效率要高得多。

最大关键字元素和最小关键字元素

我们知道,由于二叉搜索树的性质,我们很容易就可以去找出树中的最小元素和最大元素,这两个过程在一棵高度为h的树上均能在O(h)时间内完成。

 后继和前驱(SUCCESSOR,PREDECESSOR)

给定一个二叉查找树中的结点,找出在中序遍历顺序下某个节点的前驱和后继。如果树中所有关键字都不相同,则某一结点x的前驱就是小于x.key的所有关键字中最大的那个结点,后继即是大于x.key中的所有关键字中最小的那个结点。一棵二叉搜索树的结构允许我们通过没有任何关键字的比较来确定一个结点的后继。

查找前驱:先判断x是否有左子树,如果有则在left[x]中查找关键字最大的结点,即是x的前驱。如果没有左子树,则从x继续向上执行此操作,直到遇到某个结点是其父节点的右孩子结点。例如下图查找结点7的前驱结点6过程:

伪代码

查找后继步骤:先判断x是否有右子树,如果有则在x.right中查找关键字最小的结点,即为x的后继。如果没有右子树,则从x的父节点开始向上查找,直到遇到某个结点是其父结点的左儿子的结点时为止。例如下图查找结点13的后继结点15的过程。

伪代码

QQ截图20150817205320

插入和删除

插入和删除会引起二叉查找表示的动态集合的变化,难点在在插入和删除的过程中要保持二叉查找树的性质。

要将一个新值key插入到一棵二叉搜索树T中,需要调用过程TREE-INSERT。以结点z作为输入(newnode),其中z.key=key,z.left=NULL,z.right=NULL。

QQ截图20150815194752

算法复杂度:

显然,在一棵高度为h的树上的运行时间为O(h)。

中序遍历

这就是12.1-3中执行中序遍历的非递归算法。使用栈作为辅助数据结构

12.1-3  给出一个非递归的中序树遍历算法。(提示:有两种方法,在较容易的方法中,可以采用栈作为辅助数据结构;较复杂的方法中,不采用栈结构,但假设可以测试两个指针是否相等)

 删除

从二叉搜索树中删除一个结点z的整个策略可以分为三种基本情况。

  • 如果z没有孩子结点,那么将它删除,并修改它的父结点,用NULL作为孩子来替换z。

  • 如果结点z只有一个子树(左子树或者右子树),那么将这个孩子提升到树中z的位置上。删除过程如下图所示

  • 如果z有两个孩子,那么找z的后继y(一定在z的右子树中),并让y占据树中z的位置。z的原来右子树部分成为y的新的右子树,并且z的左子树成为y的新的左子树。

考虑 图12-4中的四种情况

QQ截图20150817195438

  • (a)如果z没有左孩子,那么用其右孩子来代替z。这个右孩子可以是NULL,也可以不是。当z的右孩子是NULL时,这种情况可以归为z没有孩子结点的情况。当右孩子非NULL时,即z仅有一个孩子结点的情形。
  • (b)如果z仅有一个孩子且为其左孩子,那么用其左孩子来替换z。
  • z既有一个左孩子,又有一个右孩子,查找z的后继y,这个后继位于z的右子树中并且没有左孩子。(这一点需要证明)
  • (c)如果y是z的右孩子,那么用y替换z,并仅留下y的右孩子
  • (d)否则,y位于z的右子树但并不是z的右孩子,并且z的后继y!=r位于以r为根的子树中。这种情况下,先用y的右孩子替换y,并且置y为r的parent。然后,再置y为q的孩子和r的parent。

 子过程TRANSPLANT

用另一棵子树替换一棵子树并成为其双亲的孩子结点。当TRANSPLANT用一棵以v为根的子树来替换一棵以u为根的子树时,结点u的双亲就变为结点v的双亲,并且最后v成为u的双亲的相应孩子。(这段话结合下面程序理解)

 

明天继续红黑树及AVL树

参考资料:

1)http://www.cnblogs.com/Anker/archive/2013/01/28/2880581.html

2)