前言
本章讨论一下树的查找,也就是查找树
方法
1.概念
对于查找树而言,我们最值得研究的就是二叉查找树。
二叉查找/搜索/排序树(BST)的定义:
或者是一棵空树,或者是具有下列性质的树:
- 若它的左子树不为空,则左子树所有结点的值都小于根节点的值
- 若它的右子树不为空,则右子树所有结点的值都大于根节点的值
- 它的左右子树也必须为二叉查找树
二叉查找树的示例:
上图就是二叉查找树,由定义可以知道其为二叉查找树。
当我们需要查找树中的某个结点值的时候,我们可以很方便的查找,因为其存在一定的规律。
下面的树也是二叉查找树:
同样也满足上面的条件。
2.平衡二叉树
我们引入一个新的概念,叫做二叉平衡树(AVL树)
平衡二叉树的定义:
或者是一棵空树,或者是具有下列性质的二叉查找树:
- 它的左右两个子树的高度差(平衡因子)不超过1
- 它的左右子树也必须为平衡二叉树
下面就是一个平衡二叉树:
下面则不是平衡二叉树,请大家思考原因:
平衡二叉树的目的在于减少二叉查找树的层次,提高查找的速度,其算法的时间复杂度为:log(n)
平衡二叉树有下列实现方式:AVL、红黑树、替罪羊树等
3.红黑树
红黑树是一种平衡二叉树,它具有下列性质:
- 每个结点或者是黑色或者时红色
- 根节点是黑色
- 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
红黑树示例:
4.Java中的实例
在Java中,最明显的例子就是TreeSet和TreeMap
我们首先来看一下TreeMap
TreeMap采用红黑树的数据结构来实现,树节点Entry结构如下:
很明显,相当的熟悉吧!有key,有value,还有对应的左结点、右结点、父亲结点以及默认颜色黑色。
TreeMap的大致结构:
由于红黑树需要比较结点的大小来决定插入左边还是右边,所以其内置比较器comparator
同时还有红黑树的根结点root,以及树结点的数量size
默认的构造方法如下,其指定了一个空的比较器:
这个时候将采用自然比较,即根据key的类型选择比较器进行比较。
TreeMap的put()方法实现:这里希望大家可以自主摸索,探究红黑树的真谛。
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
其查找方法也相应给出,争取啃透源码:
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
为什么先说TreeMap呢?因为TreeSet的底层实现就是TreeMap!!!
其添加方法实现如下:
实质上就是调用了TreeMap的put方法,依然采用红黑树来实现!