二叉树就不多说了,这里记录一下平衡二叉树——AVL和红黑树的定义
平衡的排序二叉树——AVL:
任何节点的左右子树高度差最多为1.满足这个平衡定义的排序二叉树被称为AVL,这个名字源于它的发明者XXXXXX
红黑二叉树:
红黑树也是一种平衡的排序二叉树,但它不是高度平衡,而是大致平衡。
它确保任意一条从根到叶子节点的路径,没有任何一条路径的长度会比其他路径长过两倍。在实际应用中,统计性能要高于AVL树
TreeMap
first:TreeMap是按键有序,而不是按值有序!
second:TreeMap内部用红黑树实现,每个节点非红即黑
- 构造方法
public TreeMap()
public TreeMap(Comparator<? super K> comparator)
第一个构造方法,要求Map中的键实现Comparable接口,TreeMap会调用Comparable接口中的compareTo方法进行内部比较
第二个接受一个比较器对象comparator,如果不为null,在TreeMap内部进行比较时会调用这个comparator的compare方法,而不再调用键的compareTo方法,也不再要求键实现Comparable接口。
1. 内部组成
private final Comparator<? super K> comparator; // 比较器,构造方法传递,如果没传,就是null。
private transient Entry<K,V> root = null; // 树的根节点,从根节点可以访问到每个节点,节点类型为Entry。
private transient int size = 0; // 当前键值对的个数
Entry是TreeMap的一个内部类:
// Red-black mechanics
private static final boolean RED = false;
private static final boolean BLACK = true;
/**
* Node in the Tree. Doubles as a means to pass key-value pairs back to
* user (see Map.Entry).
*/
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
/**
* Make a new cell with given key, value, and parent, and with
* {@code null} child links, and BLACK color.
*/
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
2. put方法主要逻辑
- root==null,添加第一个节点
- root != null
- 寻找父节点(分设置了comparator和未设置comparator)
- 找到父节点,新建一个节点,根据新的键与父节点键的比较结果,插入左/右孩子,并增加size和modCount
- 调用fixAfterInsertion方法保持树的大致平衡
3. get方法主要逻辑
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
- 根据key找到对应节点,找到节点后获取值
getEntry主要逻辑:
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;
}
- 如果comparator不为空,调用单独的方法getEntryUsingComparator
- comparator为空,假定key实现了Comparable接口,使用compareTo方法进行比较,从根开始,小于往左边找,大于往右边找,如果没有找到,返回null。(getEntryUsingComparator逻辑类似)
4. containsValue主要逻辑
containsValue需要便利进行比对
public boolean containsValue(Object value) {
for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
if (valEquals(value, e.value))
return true;
return false;
}
getFirstEntry返回第一个节点,successor返回给定节点的后继几点,valEquals比较值,从第一个开始逐个进行比较,没找到则返回false;
其中涉及的方法都比较简单,这里就不赘述了,可以去看下源码。
5. 根据键删除键值对
具体代码就不分析了,贴一个过程吧。
小结
与HashMap相比,TreeMap同样实现了Map接口,但内部使用红黑树实现。红黑树时统计效率比价高的大致平衡二叉树,这决定了它有如下特点:
- 按键有序,TreeMap同样实现了SortedMap和NavigableMap接口,可以方便地根据键的顺序进行查找,如第一个、最后一个、某一范围的键、邻近键等。
- 为了按键有序,TreeMap要求键实现Comparable接口或通过构造方法提供一个Comparable对象。
- 根据键保存、查找、删除的效率比较高,为O(h),h为树的高度,在树平衡的情况下,h为log以2为底N的对数。N为节点数;
在不要求排序的情况下,优先使用HashMap。
TreeSet
与HashSet和HashMap的关系一样,TreeSet是基于TreeMap的
构造方法:
public TreeSet()
public TreeSet(Comparator<? super E> comparator)
TreeSet经常也只是当做Set使用,只是希望迭代输出有序;
Set<String> words = new TreeSet<String>();
words.addAll(Arrays.asList("tree", "map", "hash", "map"));
for(String w:words){
System.out.print(w+" ");
}
输出为: hash map tree
TreeSet实现了两个点:去重和有序。
HashSet是基于HashMap实现的,元素时HashMap中的键,值是一个固定的值,TreeSet是类似的,它是基于TreeMap实现的。
内部成员
private transient NavigableMap<E, Object> m;
private static final Object PRESENT = new Object();
m 就是背后的那个TreeMap,这里用的是更为通用的接口类型NavigableMap,PARENT就是那个固定的值。TreeSet的方法实现主要就是调用m的方法;
默认构造方法代码:
TreeSet(NavigableMap<E,Object> m){
this.m = m;
}
public TreeSet(){
this(new TreeMap<E,Object>());
}
add方法:
public boolean add(E e){
return m.put(e,PRESENT) == null;
}
就是调用map的put方法,元素e用作键,值就是固定的PARENT,put返回null表示原来没有对应的键,添加成功了。
contains方法:
public boolean contains(Object o){
return m.containsKey(o);
}
remove方法
public boolean remove(Object o){
return m.remove(0) == PRESENT;
}