排序二叉树和TreeMap&TreeSet——读书笔记

二叉树就不多说了,这里记录一下平衡二叉树——AVL和红黑树的定义

平衡的排序二叉树——AVL:

任何节点的左右子树高度差最多为1.满足这个平衡定义的排序二叉树被称为AVL,这个名字源于它的发明者XXXXXX

红黑二叉树:

红黑树也是一种平衡的排序二叉树,但它不是高度平衡,而是大致平衡。
它确保任意一条从根到叶子节点的路径,没有任何一条路径的长度会比其他路径长过两倍。在实际应用中,统计性能要高于AVL树

TreeMap

first:TreeMap是按键有序,而不是按值有序!
second:TreeMap内部用红黑树实现,每个节点非红即黑

  1. 构造方法
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方法主要逻辑

  1. root==null,添加第一个节点
  2. root != null
    1. 寻找父节点(分设置了comparator和未设置comparator)
    2. 找到父节点,新建一个节点,根据新的键与父节点键的比较结果,插入左/右孩子,并增加size和modCount
    3. 调用fixAfterInsertion方法保持树的大致平衡

3. get方法主要逻辑

    public V get(Object key) {
        Entry<K,V> p = getEntry(key);
        return (p==null ? null : p.value);
    }

  1. 根据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;
    }
  1. 如果comparator不为空,调用单独的方法getEntryUsingComparator
  2. 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. 根据键删除键值对

具体代码就不分析了,贴一个过程吧。
end

小结

与HashMap相比,TreeMap同样实现了Map接口,但内部使用红黑树实现。红黑树时统计效率比价高的大致平衡二叉树,这决定了它有如下特点:

  1. 按键有序,TreeMap同样实现了SortedMap和NavigableMap接口,可以方便地根据键的顺序进行查找,如第一个、最后一个、某一范围的键、邻近键等。
  2. 为了按键有序,TreeMap要求键实现Comparable接口或通过构造方法提供一个Comparable对象。
  3. 根据键保存、查找、删除的效率比较高,为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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值