提交 0444b218 编写于 作者: A Abel-Huang

add 9.1.0

上级 12243b1d
# 第九章 平衡搜索
> 译者:[Abel-Huang](https://github.com/Abel-Huang)
我们之前已经看到二叉搜索树有一个弱点:左右子树趋向于不平衡,因此无法将它们所表示的数据集划分成两个同样大小的部分。我们考虑一下我们可以对此做些什么改进。
当然,我们总是可以通过简单地将所有节点按顺序进行排序,然后重新插入它们以生成一个新的平衡二叉树。然而这种操作需要花费树节点数线性规模的复杂度,并且可以看出在 插入N个元素很难避免退化为$\Theta(N^2)$的复杂度。相比之下,如果数据碰巧以保持树遍历的顺序,则进行N次插入仅需要$\Theta(N\lgN)$的时间。因此,让我们首先看看重新平衡树(或保持平衡)的操作,而不将其拆开并重新构建。
## 9.1 平衡构造:B-树
保持搜索树平衡的另一种方法是始终小心"在一个正确的位置插入新的树节点",以便树通过构造器保持平衡。数据库社区长期以来一直使用完全符合这个要求的数据结构:*B-tree*。我们将在这里抽象地描述这种数据结构和相关操作,而不是直接给出代码,因为这种数据结构用于实践中大量用于提高速度的存储设备。
*m阶B-树*是具有以下属性的多叉树:
1. 每个节点孩子数最多为m个。
2. 除根之外的所有节点至少有m/2个子节点(我们也可以说除根节点外的每个节点至少包含⌈m/2⌉个子节点)。
3. 一个节点的孩子$C_0$, $C_1$, ..., $C_(n-1)$用键$K_1$, ..., $K_(n-1)$标记(认为$K_i$在$C_(i-1)$和$C_i$之间),其中$k_1$<$k_2$<...<$k_(n-1)$;
4. B-树是一种搜索树:对于任何一个节点,以$C_i$为根的子树中所有键都严格地小于$K_(i+1)$,并且(对于i > 0)严格地大于$K_i$。
5. 所有空孩子都出现在树的同一层。
图9.1包含一个4阶排序树的示例。在实际实现中,B-树一般用在在二级存储(如磁盘等)上,根据实际需要读入它们的节点。我们选择m阶树(多叉数)的目的是尽可能快地从二级存储器传输数据。对于磁盘操作而言更关注的是单次磁盘访问所需要时间越小越好,
因此m的值范围越大,从不同节点中读取的时间差异越小。在这种情况下,**m**取的值过小很明显不是一个很好的主意。
我们将用一个被称为**BTreeNode**的结构来表示B-树的节点,并且有如下的定义:
**B.child(i)**:B-树节点**B**的第*i*个孩子,**0 <= i < m**
**B.key(i)**:B-树节点**B**的第*i*个关键字Key,**1 <= i < m**
**B.parent()**:B-树节点**B**的父节点。
**B.index()**:B-树节点**B**的索引*i***B == B.parent().child(i)**
**B.arity()**:B-树节点**B**的孩子数。
然后,整个B-树由指向根的指针组成,可能还包含一些额外的有用信息,例如B-树当前的大小。
基于属性2和属性5,一个拥有N个关键字的B-树一定有$\Theta(\log_(m/2)N)$层节点。基于属性1,搜索单个节点的关键字需要O(1)时间(保证m为固定值)。因此通过下面的递归算法搜索B-树是一个$\Theta(\lgN)$级别的操作。
```java
boolean search (BTreeNode B, Key X) {
if (B is the empty tree)
return false;
else {
Find largest c such that B.key(i) X, for all 1 i c.
if (c > 0 && X.equals (B.key (c)))
return true;
else
return search (B.child (c), K);
}
```
![figure_9_1](https://github.com/Abel-Huang/cs61b-textbook-zh/blob/master/zh/img/figure_9_1.png)
图9.1: 键为整型的4阶B-树示例。圆圈代表空节点,它们出现在树的同一层级。每个节点拥有2到4个子节点,每个节点用于1到3个关键字。每个键都大于其左侧子节点中的所有键,并且小于右侧子节点中的所有键
### 9.1.1 插入
### 9.1.2 删除
![figure_9_2](https://github.com/Abel-Huang/cs61b-textbook-zh/blob/master/zh/img/figure_9_2.jpg)
### 9.1.3 红黑树
## 9.2 字典树
### 9.2.1 基本属性和算法
```java
public abstract class Trie {
/** The empty Trie. */
public static final Trie EMPTY = new EmptyTrie();
/** The label at this node. Defined only on leaves. */
abstract public String label();
/** True if X is in this Trie. */
public boolean isIn(String x) ...
/** The result of inserting X into this Trie, if it is not
* already there, and returning this. This trie is
* unchanged if X is in it already. */
public Trie insert(String x) ...
/** The result of removing X from this Trie, if it is present.
* The trie is unchanged if X is not present. */
public Trie remove(String x) ...
/** True if this Trie is a leaf (containing a single String). */
abstract public boolean isLeaf();
/** True if this Trie is empty */
abstract public boolean isEmpty();
/** The child numbered with character K. Requires that this node
* not be empty. Child 0 corresponds to ✷. */
abstract public Trie child(int k);
/** Set the child numbered with character K to C. Requires that
* this node not be empty. (Intended only for internal use. */
abstract protected void setChild(int k, Trie C);
}
```
```java
/** True if X is in this Trie. */
public boolean isIn(String x) {
Trie P = longestPrefix(x, 0);
return P.isLeaf() && x.equals(P.label());
}
/** The node representing the longest prefix of X.substring(K) that
* matches a String in this trie. */
private Trie longestPrefix(String x, int k) {
if (isEmpty() || isLeaf())
return this;
int c = nth(x, k);
if (child(c).isEmpty())
return this;
else
return child(c).longestPrefix(x, k+1);
}
/** Character K of X, or ✷ if K is off the end of X. */
static char nth(String x, int k) {
if (k >= x.length())
return (char) 0;
else
return x.charAt(k);
}
```
### 9.2.2 表示
```java
class EmptyTrie extends Trie {
public boolean isEmpty() { return true; }
public boolean isLeaf() { return false; }
public String label() { throw new Error(...); }
public Trie child(int c) { throw new Error(...); }
protected void child(int c, Trie T) { throw new Error(...); }
}
class LeafTrie extends Trie {
private String L;
/** A Trie containing just the string S. */
LeafTrie(String s) { L = s; }
public boolean isEmpty() { return false; }
public boolean isLeaf() { return true; }
public String label() { return L; }
public Trie child(int c) { return EMPTY; }
protected void child(int c, Trie T) { throw new Error(...); }
}
class InnerTrie extends Trie {
// ALPHABETSIZE has to be defined somewhere */
private Trie[] kids = new kids[ALPHABETSIZE];
/** A Trie with child(K) == T and all other children empty. */
InnerTrie(int k, Trie T) {
for (int i = 0; i < kids.length; i += 1)
kids[i] = EMPTY;
child(k, T);
}
public boolean isEmpty() { return false; }
public boolean isLeaf() { return false; }
public String label() { throw new Error(...); }
public Trie child(int c) { return kids[c]; }
protected void child(int c, Trie T) { kids[c] = T; }
}
```
### 9.2.3 表压缩
```java
class InnerTrie extends Trie {
private static char[] charMap = new char[9+1];
static {
charMap[0] = 0;
charMap[0] = 1; charMap[1] = 1; ...
}
public Trie child(int c) { return kids[charMap[c]]; }
protected void child(int c, Trie T) { kids[charMap[c]] = T; }
}
```
## 9.3 旋转自平衡树
### 9.3.1 AVL树
## 9.4 伸展树
### 9.4.1 分析
## 9.5 跳表
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册