TreeMap源码分析解读

TreeMap

平衡二叉树

平衡二叉树(Self-balancing binary search tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,同时,平衡二叉树必定是二叉搜索树,反之则不一定。平衡二叉树的常用实现方法有红黑树AVL替罪羊树Treap伸展树等。 最小二叉平衡树的节点的公式如下 F(n)=F(n-1)+F(n-2)+1 这个类似于一个递归的数列,可以参考Fibonacci(斐波那契)数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。

我们知道,对于一般的二叉搜索树(Binary Search Tree),其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度(O(log2n))同时也由此而决定。但是,在某些极端的情况下(如在插入的序列是有序的时),二叉搜索树将退化成近似链表或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作之后,由于在删除时,我们总是选择将待删除节点的后继代替它本身,这样就会造成总是右边的节点数目减少,以至于树向左偏沉。这同时也会造成树的平衡性受到破坏,提高它的操作的时间复杂度。

平衡二叉搜索树(Balanced Binary Tree)具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。常用算法有红黑树、AVL、Treap、伸展树等。在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在O(log(n)),大大降低了操作的时间复杂度。

  • 红黑树

红黑树(Red Black Tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为”对称二叉B树”,它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的:它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。

  • AVL

AVL是最先发明的自平衡二叉查找树算法。在AVL中任何节点的两个儿子子树的高度最大差别为一,所以它也被称为高度平衡树,n个结点的AVL树最大深度约1.44log2n。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

  • Treap

Treap是一棵二叉排序树,它的左子树和右子树分别是一个Treap,和一般的二叉排序树不同的是,Treap纪录一个额外的数据,就是优先级。Treap在以关键码构成二叉排序树的同时,还满足堆的性质(在这里我们假设节点的优先级大于该节点的孩子的优先级)。但是这里要注意的是Treap和二叉堆有一点不同,就是二叉堆必须是完全二叉树,而Treap并不一定是。

  • 伸展树

伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它由Daniel Sleator和Robert Tarjan创造。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于伸展操作。

TreeMap签名

public class TreeMap<K,V>
   extends AbstractMap<K,V>
   implements NavigableMap<K,V>, Cloneable, java.io.Serializable

相比HashMap来说,TreeMap多实现了一个接口NavigableMap,也就是这个接口,决定了TreeMap与HashMap的不同:HashMap的key是无序的,TreeMap的key是有序的。

public interface NavigableMap<K,V> extends SortedMap<K,V>

发现NavigableMap继承了SortedMap,再看SortedMap的签名

public interface SortedMap<K,V> extends Map<K,V>

SortedMap就像其名字那样,说明这个Map是有序的。这个顺序一般是指由Comparable接口提供的keys的自然序(natural ordering),或者也可以在创建SortedMap实例时,指定一个Comparator来决定。 当我们在用集合视角(collection views,与HashMap一样,也是由entrySet、keySet与values方法提供)来迭代(iterate)一个SortedMap实例时会体现出key的顺序。

  • Comparable一般表示类的自然序,比如定义一个Student类,学号为默认排序
  • Comparator一般表示类在某种场合下的特殊分类,需要定制化排序。比如现在想按照Student类的age来排序

插入SortedMap中的key的类都必须实现Comparable接口(或指定一个comparator),这样才能确定如何比较(通过k1.compareTo(k2)或comparator.compare(k1, k2))两个key,否则,在插入时,会报ClassCastException的异常。 此外,SortedMap中key的顺序性应该与equals方法保持一致。也就是说k1.compareTo(k2)或comparator.compare(k1, k2)为true时,k1.equals(k2)也应该为true。 介绍完了SortedMap,再来回到我们的NavigableMap上面来。 NavigableMap是JDK1.6新增的,在SortedMap的基础上,增加了一些“导航方法”(navigation methods)来返回与搜索目标最近的元素。例如下面这些方法:

  • lowerEntry,返回所有比给定Map.Entry小的元素
  • floorEntry,返回所有比给定Map.Entry小或相等的元素
  • ceilingEntry,返回所有比给定Map.Entry大或相等的元素
  • higherEntry,返回所有比给定Map.Entry大的元素

TreeMap计理念

红黑树(Red–black tree)

TreeMap是基于红黑树结构实现的一种Map,要分析TreeMap的实现首先就要对红黑树有所了解。红黑树是一种自平衡二叉查找树。

二叉查找树
  • 若左子树不为空,则左子树上所有节点的值均小于它的根节点的值;
  • 若右子树不为空,则右子树上所有节点的值均大于它的根节点的值;
  • 左、右子树也分别为二叉查找树;
  • 没有键值相等的节点。

按照二叉查找树存储的数据,对元素的搜索效率是非常高的,比如上图中如果要查找值为48的节点,只需要遍历4个节点就能完成。

用代码实现简单的二叉查找树:

public class BinaryTree {

    // 二叉树的根节点
    public TreeNode rootNode;
    // 记录搜索深度
    public int count;

    /**
     * 利用传入一个数组来建立二叉树
     */
    public BinaryTree(int[] data) {
        for (int i = 0; i < data.length; i++) {
            addNodeToTree(data[i]);
        }
    }

    /**
     * 将指定的值加入到二叉树中适当的节点
     */
    private void addNodeToTree(int value) {
        TreeNode currentNode = rootNode;
        // 建立树根
        if (rootNode == null) {
            rootNode = new TreeNode(value);
            return;
        }

        // 建立二叉树
        while (true) {
            // 新增的value比节点的value小,则在左子树
            if (value < currentNode.value) {
                if (currentNode.leftNode == null) {
                    currentNode.leftNode = new TreeNode(value);
                    return;
                } else {
                    currentNode = currentNode.leftNode;
                }
            } else { // 新增的value比节点的value大,在右子树
                if (currentNode.rightNode == null) {
                    currentNode.rightNode = new TreeNode(value);
                    return;
                } else {
                    currentNode = currentNode.rightNode;
                }
            }
        }
    }

    /**
     * 中序遍历(左子树 -树根- 右子树)
     */
    public void inOrder(TreeNode node) {
        if (node != null) {
            inOrder(node.leftNode);
            System.out.print("[" + node.value + "]");
            inOrder(node.rightNode);
        }
    }

    /**
     * 前序遍历(树根 -左子树- 右子树)
     */
    public void preOrder(TreeNode node) {
        if (node != null) {
            System.out.print("[" + node.value + "]");
            preOrder(node.leftNode);
            preOrder(node.rightNode);
        }
    }

    /**
     * 后序遍历(左子树 -右子树- 树根)
     */
    public void postOrder(TreeNode node) {
        if (node != null) {
            postOrder(node.leftNode);
            postOrder(node.rightNode);
            System.out.print("[" + node.value + "]");
        }
    }

    /**
     * 从二叉树中查找指定value
     */
    public boolean findTree(TreeNode node, int value) {
        if (node == null) {
            Syste
  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
TreeMapJava中的一种有序映射数据结构,它基于红黑树实现。它可以根据键的自然顺序或自定义比较器对键进行排序,并且键是唯一的。TreeMap提供了一些方法来支持协同进化分析。 1. 插入元素:当向TreeMap中插入一个键值对时,它会根据键的顺序将其插入到正确的位置。如果键已经存在,则会更新对应的值。 2. 删除元素:可以使用remove(key)方法从TreeMap中删除指定的键值对。删除操作会保持树的平衡。 3. 获取元素:可以使用get(key)方法从TreeMap中获取指定键对应的值。如果键不存在,则返回null。 4. 遍历元素:可以使用entrySet()方法获取TreeMap中的所有键值对,并使用迭代器或增强for循环遍历它们。遍历结果是按照键的顺序排列的。 5. 子映射操作:TreeMap提供了一些方法来获取子映射,例如headMap(key)、tailMap(key)和subMap(fromKey, toKey)。这些方法可以返回满足指定条件的键值对子集合。 6. 寻找最小和最大键:可以使用firstKey()和lastKey()方法分别获取TreeMap中的最小和最大键。 7. 寻找前一个和后一个键:可以使用lowerKey(key)和higherKey(key)方法分别获取TreeMap中小于和大于指定键的最接近的键。 8. 比较器:如果没有提供自定义的比较器,TreeMap会使用键的自然顺序进行排序。如果提供了自定义的比较器,它将根据比较器的规则进行排序。 9. 性能注意事项:由于TreeMap是基于红黑树实现的,插入和删除操作的时间复杂度为O(log n),其中n是TreeMap中的元素个数。因此,在大量插入和删除操作的场景下,性能可能会受到影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值