第三章 查找(二) - 《算法》读书笔记
目录
第三章 查找(二)
3.2 二叉查找树
一颗二叉查找树(BST)是一颗二叉树,其中每个节点都含有一个Comparable的键(以及相关联的值)且每个结点的值都大于其左子树中的任意节点的键,而小于右子树的任意结点的键。
3.2.1 基本实现
public class BST<Key extends Comparable<Key>, Value>{
private Node root;
private class Node{
private Key key;
private Value val;
private Node left, right;
private int N;
public Node(Key key, Value val, int N){
this.key = key; this.val = val; this.N = N;
}
}
public int size(){
return size(root);
}
private int size(Node x){
if (x == null) return 0;
else return x.N;
}
public Value get(Key key){
return get(root, key);
}
private Value get(Node x, Key key){
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp < 0) return get(x.left, key);
else if (cmp > 0) return get(x.right, key);
else return x.val;
}
public void put(Key key, Value val){
root = put(root, key.val);
}
private Node put(Node x, Key key, Value val){
if (x == null) return new Node(key, val, 1);
int cmp = key.compareTo(x.key);
if (cmp < 0) x.left = put(x.left, key, val);
else if (cmp > 0) x.right = put(x.right, key, val);
else x.val = val;
x.N = size(x.left) + size(x.right) + 1;
return x;
}
}
3.2.2 分析
在由N个随机键构造的二叉查找树中,查找命中平均所需的比较次数为~2lnN(约1.39lgN)。
在由N个随机键构造的二叉查找树中,插入操作和查找未命中平均所需的比较次数为~2lnN(约1.39lgN)。
3.2.3 有序性相关的方法与删除操作
3.2.3.2 向上取整和向下取整
- 如果给定的键key小于二叉查找树的根结点的键,那么小于等于key的最大键floor(key)一定在根结点的左子树中;如果给定的键key大于二叉查找树的根结点,那么只有当根结点右子树中存在小于等于key的结点时,小于等于key的最大键才会出现在右子树中,否则根结点就是小于等于key的最大键。
- 具体实现如下:
public Key floor(Key key){
Node x = floor(root, key);
if (x == null) return null;
return x.key;
}
private Node floor(Node x, Key key){
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp == 0) return x;
if (cmp < 0) return floor(x.left, key);
Node t = floor(x.right, key);
if (t != null) return t;
else return x;
}
3.2.3.5 删除最大键和删除最小键
- 对于deleteMin(),我们要不断深入根结点的左子树中,直至遇见一个空链接,然后将该结点的连接指向该节点的右子树(只需要在递归调用中返回它的右连接即可)。
- 具体实现如下:
public void deleteMin(){
root = deleteMin(root);
}
private Node deleteMin(Node x){
if (x.left == null) return x.right;
x.left = deleteMin(x.left);
x.N = size(x.left) + size(x.right) + 1;
return x;
}
3.2.3.6 删除操作
- 对于拥有两个子结点的结点,在删除结点x后,用它的后继结点填补它的位置。因为x有一个右子结点,因此它的后继结点就是其右子树中的最小结点。具体步骤如下:
- 将指向即将被删除的结点的链接保存为t
- 将x指向它的后继结点min(t.right)
- 将x的右链接(原本指向一颗所有结点都大于x.key的二叉查找树)指向deleteMin(t.right),也就是在删除后所有结点仍然都大于x.key的子二叉查找树
- 将x的左链接(本为空)设为t.left(其下所有的键都小于被删除的结点和它的后继结点)
- 具体实现如下:
public void delete(Key key){
root = delete(root, key);
}
private Node delete(Node x, Key key){
if (x == null) return null;
int cmp = key.compareTo(x.key);
if (cmp < 0) x.left = delete(x.left, key);
else if (cmp > 0) x.right = delete(x.right, key);
else{
if (x.right == null) return x.left;
if (x.left == null) return x.right;
Note t = x;
x = min(t.right);
x.right = deleteMin(t.right);
x.left = t.left;
}
x.N = size(x.left) + size(x.right) + 1;
return x;
}
3.2.3.8 性能分析
在一颗二叉查找树中,所有操作在最坏情况下所需的时间都和树的高度成正比。

浙公网安备 33010602011771号