二叉搜索树概述
搜索树数据结构支持许多动态集合操作:SEARCH
、MINIMUM
、MAXIMUM
、PREDECESSOR
、SUCCESSOR
、INSERT
与DELETE
。
上述基本操作花费的时间与这棵树的高度成正比。
定义与性质
(1)设x
为二叉查找树中的一个结点,若y
是x
左子树中的一个结点,则key[y] <= key[x
];若y
是x
右子树中的一个结点,则key[x]<=key[y]
(2)二叉查找树上执行的基本操作的时间与树的高度成正比。
结构
每个节点就是一个对象,包含:关键字key
,卫星数据data
,以及三个属性:分别指向父、左右孩子的指针p
,left
, right
在二叉查找树上的操作
查找一个关键字:SEARCH(x, k)
求最小关键字:MINIMUM(x)
求最大关键字:MAXIMUM(x)
求前驱:PREDECESSOR(x)
求后继:SUCCESSOR(x)
插入一个结点:INSERT(T, z)
删除一个结点:DELETE(z)
二叉查找树的应用
1.遍历:中序遍历、先序遍历、后序遍历【根关键字遍历的先后顺序】
2.查找:查找包含某个关键字的结点,查找关键字最大或最小的结点、查找某个结点的前驱或后继
12.1-2
二叉搜索树的性质与最小堆的性质有什么不同?
二叉搜索树:左子树关键字<=根结点关键字<=右子树关键字
最小堆:左子树关键字>=根结点关键字 && 右子树关键字>=根结点关键字
不能,因为一个结点的的左子树与右子树的关键字大小没有关系
12.1-3 遍历的非递归实现
给出一个非递归的中序树遍历算法。(提示:有两种方法,在较容易的方法中,可以采用栈作为辅助数据结构;较复杂的方法中,不采用栈结构,但假设可以测试两个指针是否相等)。
中根遍历要求顺序是左根右,借助栈s实现。先将根root入栈,接着从根root开始查找最左的子孩子结点直到为空为止,然后将空节点出栈,再将左子树节点出栈遍历,然后判断该左子树的右子树节点入栈。循环此过程,直到栈为空为止。此时需要注意的是入栈过程中空结点也入栈了,用以判断左孩子是否结束和左孩子是否有右孩子结点。
一个更加简洁的写法:
查询二叉搜索树
查找任一节点
伪码:
运行时间为$O(h)$,h为树的高度。
最大关键字元素和最小关键字元素
根据二叉查找树的特征,很容易查找出最大和最小关键字。
查找二叉树中的最小关键字:从根结点开始,沿着各个节点的left指针查找下去,直到遇到NULL时结束。如果一个结点x无左子树,则以x为根的子树中,最小关键字就是key[x]。
查找二叉树中的最大关键字:从根结点开始,沿着各个结点的right指针查找下去,直到遇到NULL时结束。
书中给出了查找最大最小关键字的伪代码:
|
|
均能在$O(h)$时间内执行完。
后继与前驱
伪码:
x.p 指向双亲。 右侧为空,则向上搜索。
在一颗高度为
h
的二叉搜索树上,动态集合上的操作SEARCH
、MINIMUM
、MAXIMUM
、SUCCESSOR
和PREDECEDOR
可以在$O(h)$时间内完成。
插入和删除
插入
伪码:
从树根开始,指针x记录了一条向下的简单路径,直到查找到要替换为输入项z的NIL。NIL占据的位置就是z放置的位置。
上述过程保持遍历指针(trailing pointer
)y作为x的双亲,找到NIL时需要直到z属于哪个节点。
该过程可以在$O(h)$时间内完成。
删除
从二叉查找树中删除给定的结点z,分以下情况讨论:
- 如果z没有左孩子,那么用右孩子来替换z
- 如果z有且仅有一个左孩子,那么用其左孩子替换z
- 否则,z既有左孩子又有右孩子,此时我们查找z 的后继y,这个后继位于z的右子树中并且没有左孩子。则将y移出原来的位置进行拼接,并替换数中的z。
- 如果y是z的右孩子,那么用y替换z,并且仅留下y的右孩子。
伪码:
定理12.3 在一颗高度为
h
的二叉搜索树上,实现动态集合操作INSERT
和DELETE
的运行时间均为$O(h)$。
随机构建二叉搜索树
二叉查找树各种操作时间均是$O(h)$,构建二叉查找树时,一般只用插入函数,这样便于分析,如果按严格增长顺序插入,那么构造出来的树就是一个高度为n-1
的链。另一方面练习B.5-4
说明了h≥lgn
.这里我特别证明下。
证明:一个有n个结点的非空二叉树的高度至少为lgn
。
对于一个高度为h
的二叉树总结点数至多为n≤2^h-1
(等于的情况就是完全二叉树),所以给这个不等式适当变型得:h≥lg(n+1)≥lgn
,所以对于n
个结点的数高度至少为lgn
。虽然没有用归纳法,但是这种方法感觉简单易懂。
定理12.4 一棵有
n
个不同关键字的随机构建二叉搜索树(random built binary search tree
)的期望高度为O(lgn)
。