Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenDocCN
cs61b-textbook-zh
提交
518c37d9
C
cs61b-textbook-zh
项目概览
OpenDocCN
/
cs61b-textbook-zh
通知
3
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
cs61b-textbook-zh
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
518c37d9
编写于
5月 06, 2019
作者:
Y
YYF
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
0506
上级
c9ccca6c
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
235 addition
and
11 deletion
+235
-11
zh/6.md
zh/6.md
+235
-11
zh/img/6.1.png
zh/img/6.1.png
+0
-0
zh/img/6.3.png
zh/img/6.3.png
+0
-0
zh/img/6.8.png
zh/img/6.8.png
+0
-0
未找到文件。
zh/6.md
浏览文件 @
518c37d9
# 第六章 搜索树
# 第六章 搜索树
Search Trees
> 译者:[YYF](https://github.com/yongfengyan)
...
...
@@ -12,11 +12,13 @@
图6.1a是典型BST的示例。在该示例中,标签是整数,键与标签相同,关于“小于”、“大于”和“等于”的表达为其通常的含义。
![
avatar
](
/img/6.1.png
)
键不必是整数。通常,我们可以对key使用任何一种全序关系(total order)来把一组数据添加到BST中,我们用符号
\l
eq表述全序关系,全序关系具有以下属性:
*
完备性: 对于任何 x 和 y 要么满足 x \leq y 要么满足 y \leq x 或者二者同时都满足
*
传递性: 如果 x \leq y 而且 y \leq z 那么 x \leq z
*
非对称性 如果 x \leq y 并且 y \leq x 那么 x = y
*
完备性: 对于任何 x 和 y 要么满足 x
\l
eq y 要么满足 y
\l
eq x 或者二者同时都满足
*
传递性: 如果 x
\l
eq y 而且 y
\l
eq z 那么 x
\l
eq z
*
非对称性 如果 x
\l
eq y 并且 y
\l
eq x 那么 x = y
例如,键可以是整数,而且不仅可以是整数,可以有它们通常的含义。或者数据和键可以是字符串,其顺序是字典排序。或者,数据可以是(pairs)一对(A,B),键可以是对的第一项。字典排序就像是按定义的单词排序,而不考虑它们的含义。最后一个排序是一个例子,在这个例子中,人们可能期望在搜索树中有几个具有相同键的不同项。
...
...
@@ -39,9 +41,9 @@ static void sort(SomeType[] A) {
}
```
someType类型的数据,根据BST定义的要求,用来表示具有小于和等于运算性质的数据。
*someType类型的数据,根据BST定义的要求,用来表示具有小于和等于运算性质的数据。*
## 6.1 BST的应用
## 6.1 BST的应用
Operations on a BST
由于BST是二叉树,我们可以使用5.2节中的表示来给图6.2这个类。现在我将使用int类型作为标签,并假定标签与键相同。
...
...
@@ -93,10 +95,10 @@ class BST {
}
}
```
6.
2: A BST representation
*6.2: A BST representation*
### 6.1.1 搜索 BST
### 6.1.1 搜索 BST
Searching a BST
搜索BST与数组中进行二分搜索非常相似,树的根对应于数组的中间值。
...
...
@@ -111,7 +113,7 @@ public static BST find(BST T, int L){
}
```
### 6.1.2 插入 BST
### 6.1.2 插入 BST
Inserting into a BST
正如之前所说,使用一棵树的好处是向它添加数据时花销相对较小,如下面的例行程序。
...
...
@@ -130,9 +132,231 @@ static BST insert(BST T, int L){
return
T
;}
```
因为我写这个的特殊方式,当我在树中插入同一个值的多个副本时,它们总是在所有现有副本的右边。我将在删除操作中保留此属性。
*因为我写这个的特殊方式,当我在树中插入同一个值的多个副本时,它们总是在所有现有副本的右边。我将在删除操作中保留此属性。*
### 6.1.3 删除 BST 中的数据单元 Deleting items from a BST
删除要复杂得多,因为当你删除一个内部节点时,你不能让它的子节点失效,而是必须在树的某个地方重新连接它们。
*
显然,删除一个外部节点很容易;只需用空树替换它(见图6.3(a))。
*
删除一个只有一个叶子节点的内部节点也很容易,只需要让子节点继承父节点并且向上移动即可。(图6.3(b))
*
当两个叶子节点都不为空时,我们要先序遍历他的右子树,使第一个节点也就是key最小的节点来替代要删除的节点
此外,由于它是第一个按顺序排列的节点,其左子节点将为空。因此,我们可以用其正确的子节点替换该节点,并将
其key移动到要删除的节点,如图6.3(c)所示
![
avatar
](
/img/6.2.png
)
三个可能的删除,每个从图6.1中的树开始
下面为从BST中删除数据的一个例子,辅助函数swapsmallest()是一个附加的私有方法,定义如下:
```
java
/** Delete the instance of label L from T that is closest to
* to the root and return the modified tree. The nodes of
* the original tree may be modified. */
public
static
BST
remove
(
BST
T
,
int
L
)
{
if
(
T
==
null
)
return
null
;
if
(
L
<
T
.
label
)
T
.
left
=
remove
(
T
.
left
,
L
);
else
if
(
L
>
T
.
label
)
T
.
right
=
remove
(
T
.
right
,
L
);
// Otherwise, we’ve found L
else
if
(
T
.
left
==
null
)
return
T
.
right
;
else
if
(
T
.
right
==
null
)
return
T
.
left
;
else
T
.
right
=
swapSmallest
(
T
.
right
,
T
);
return
T
;
}
/** Move the label from the first node in T (in an inorder
* traversal) to node R (over-writing the current label of R),
* remove the first node of T from T, and return the resulting tree.*/
private
static
BST
swapSmallest
(
BST
T
,
BST
R
)
{
if
(
T
.
left
==
null
)
{
R
.
label
=
T
.
label
;
return
T
.
right
;
}
else
{
T
.
left
=
swapSmallest
(
T
.
left
,
R
);
return
T
;
}
}
```
*Removing items from a BST without parent pointers 从没有父指针的BST中删除项*
### 6.1.4 带有父指针的操作 Operations with parent pointers
如果我们修改BST类以提供父操作,并在表示中添加相应的父参数,那么操作将变得更复杂,但提供了更多的灵活性。
但不为bst提供setParent操作可能是明智的,因为使用此操作很容易破坏二进制搜索树属性。
而且因为存在插入和删除的操作,使用BST时或许根本不需要他。
因为查询操作忽略父节点所以不会受到影响,另一方面,由于在BST插入时必须设置任何一个插入节点的父节点,因此
情况变得复杂,下面展示了一种方法。当然,和之前一样,从带有父指针的BST中删除数据是最棘手的。
```
java
static
BST
insert
(
BST
T
,
int
L
)
{
BST
newNode
;
if
(
T
==
null
)
return
new
BST
(
L
,
null
,
null
);
if
(
L
<
T
.
label
)
T
.
left
=
newNode
=
insert
(
T
.
left
,
L
);
elseT
.
right
=
newNode
=
insert
(
T
.
right
,
L
);
newNode
.
parent
=
T
;
return
T
;
}
```
*Insertion into a BST that has parent pointers (带有父指针BST的插入操作)*
```
java
/** Delete the instance of label L from T that is closest to
* to the root and return the modified tree. The nodes of
* the original tree may be modified. */
public
static
BST
remove
(
BST
T
,
int
L
)
{
if
(
T
==
null
)
return
null
;
BST
newChild
;
newChild
=
null
;
result
=
T
;
if
(
L
<
T
.
label
)
T
.
left
=
newChild
=
remove
(
T
.
left
,
L
);
else
if
(
L
>
T
.
label
)
T
.
right
=
newChild
=
remove
(
T
.
right
,
L
);
// Otherwise, we’ve found L
else
if
(
T
.
left
==
null
)
return
T
.
right
;
else
if
(
T
.
right
==
null
)
return
T
.
left
;
else
T
.
right
=
newChild
=
swapSmallest
(
T
.
right
,
T
);
if
(
newChild
!=
null
)
newChild
.
parent
=
T
;
return
T
;
}
private
static
BST
swapSmallest
(
BST
T
,
BST
R
)
{
if
(
T
.
left
==
null
)
{
R
.
label
=
T
.
label
;
return
T
.
right
;
}
else
{
T
.
left
=
swapSmallest
(
T
.
left
,
R
);
if
(
T
.
left
!=
null
)
T
.
left
.
parent
=
T
;
return
T
;
}
}
```
*Removing items from a BST with parent pointers 带有父指针的删除操作*
### 6.1.5 简并性问题 Degeneracy strikes
不幸的是,事情不可能一帆风顺。
图6.1(b)中的树是按升序将节点插入到树中的结果(显然,同样的树可以从更大的树中进行适当的删除操作得到)
您可以看到在树上执行搜索或插入就像在链接列表上执行搜索或插入。它像一个链接列表,但在每个元素中都有多余的指针,这些指针始终为空
这棵树是不平衡的:它包含子树,在其中,左、右子树有不同的高度。在进行进一步学习后,我们将在第9章中回到这个问题
![
avatar
](
/img/6.1.png
)
## 6.2 实现SortedSet接口 Implementing the SortedSet interface
标准Java库接口SoretSeT(参见第2.2.4)提供了一种支持范围查询的集合。也就是说,根据某种排序关系,程序可以使用接口查找集合中某个值范围内的所有项。搜索单个特定值只是一种特殊情况,其中范围只包含一个值。使用二进制搜索树作为表示来实现这个接口是相当容易的;我们将把结果称为BSTSet
让我们提前计划一下,在这些操作中,我们必须支持headSet()、 tailSet()和 subSet(),
它们返回与该集合的子范围相关的一些底层集合的视图。
返回的值本身就是完整的排序集,也应该修改底层集。由于一个完整的集合也可以被看作是一个范围的视图,在这个范围内,边界是“非常小”到“非常大”,我们可能会寻找一个支持两个集合的表示,这两个集合都是由一个构造器创建的,而那些是其他集合的视图。这表示我们集合的一个表示,其中包含指向BST根的指针,两个边界表示集合中最大和最小的成员,空值表示缺少的边界。
因为一个重要的原因,我们将BST的根作为一个(永久的)哨兵节点。
我们将对集合的所有视图使用相同的树。如果我们表示意味着指向包含数据的树的根,那么每当删除树的节点时,该指针就必须更改。
但是,我们还必须确保更新集合中所有其他视图中的根指针,因为它们也应该反映集合中的更改。
通过引入由所有视图共享且从未删除的哨兵节点,我们便解决了保持所有视图都是最新的问题。
这是旧计算机科学格言的一个典型例子:大多数技术问题都可以通过引入另一个间接层次来解决。
*Most technical problems can be solved by introducing another level of indirection*
假设我们使用父指针,一个迭代器通过一个集合可以包含一个指针,指向下一个要返回标签的节点,一个指向最后一个节点的指针,
它的标签被返回(用于IMP删除),并且一个指向BSTSET的指针被迭代(在Java中通过使迭代器成为一个内部类方便地提供)。
迭代器将按顺序进行,跳过树中超出集合边界的部分。另请参见练习5.2关于使用父指针迭代的内容
下例说明了一个BSTSet,它显示了表示的主要元素:原始集合、包含其数据的BST、同一集合的视图以及该视图上的迭代器。这些集合都包含用于比较器的空间(见§2.2.4),以允许集合的用户指定一个顺序;在下例中,我们使用自然顺序(natural ordering),在字符串上,自然顺序给出了词典编纂顺序。图6.7包含表示的相应Java声明的草图。
```
java
public
class
BSTSet
<
T
>
extends
AbstractSet
<
T
>
{
/** The empty set, using COMP as the ordering. */
public
BSTSet
(
Comparator
<
T
>
comp
)
{
comparator
=
comp
;
low
=
high
=
null
;
sent
=
new
BST
();
}
/** The empty set, using natural ordering. */
public
BSTSet
()
{
this
(
null
);
}
/** The set initialized to the contents of C, with natural order. */
public
BSTSet
(
Collection
<?
extends
T
>
c
)
{
addAll
(
c
);
}
/** The set initialized to the contents of S, same ordering. */
public
BSTSet
(
SortedSet
<?
extends
T
>
s
)
{
this
(
s
.
comparator
());
addAll
(
c
);
}
···
/** Value of comparator(); null if naturally ordered. */
private
Comparator
<
T
>
comp
;
/** Bounds on elements in this class, null if no bounds. */
private
T
low
,
high
;
/** Sentinel of BST containing data. */
private
final
BST
<
T
>
sent
;
```
*Java representation for BSTSet class, showing only constructors and instance variables*
```
java
/** Used internally to form views. */
private
BSTSet
(
BSTSet
<
T
>
set
,
T
low
,
T
high
)
{
comparator
=
set
.
comparator
();
this
.
low
=
low
;
this
.
high
=
high
;
this
.
sent
=
set
.
sent
;
}
/** An iterator over BSTSet. */
private
class
BSTIter
<
T
>
implements
Iterator
<
T
>
{
/** Next node in iteration to yield. Equals the sentinel node* when done. */
BST
<
T
>
next
;
/** Node last returned by next(), or null if none, or if remove()
* has intervened. */
BST
<
T
>
last
;
BSTIter
()
{
last
=
null
;
next
=
first
node
that
is
in
bounds
,
or
sent
if
none
;
}
···
}
/** A node in the BST */
private
static
class
BST
<
T
>
{
T
label
;
BST
<
T
>
left
,
right
,
parent
;
/** A sentinel node */
BST
()
{
label
=
null
;
parent
=
null
;
}
BST
(
T
label
,
BST
<
T
>
left
,
BST
<
T
>
right
)
{
this
.
label
=
label
;
this
.
left
=
left
;
this
.
right
=
right
;
}
}
```
*continued: Private nested classes used in implementation*
![
avatar
](
/img/6.1.png
)
*表示的BST部分在动物群和子集之间共享。三角形表示整个子树,圆形矩形表示单个节点。每个集合包含一个指向列表根的指针(一个sentinel节点,其标签被认为大于树中的任何值)、值的上下限(空表示未绑定)和比较器(在本例中为空,表示自然顺序)。迭代器包含一个指向子集的指针(它正在迭代),一个指向按顺序包含next label的节点的指针(next)(“duck”),另一个指向按i.next()最后传递的序列包含标签的节点的指针(last)。BST的虚线区域完全由迭代器进行sk ipped。迭代器不返回“hartebeest”节点,但迭代器必须通过它才能到达它返回的节点。*
## 6.3 Orthogonal Range Queries
### 6.1.3
...
...
zh/img/6.1.png
0 → 100644
浏览文件 @
518c37d9
25.8 KB
zh/img/6.3.png
0 → 100644
浏览文件 @
518c37d9
44.5 KB
zh/img/6.8.png
0 → 100644
浏览文件 @
518c37d9
48.6 KB
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录