JAVA基础(八)TreeMap

概述

  • TreeMap 是一个有序的key-value集合,它是按照提供的比较器进行比较,没有添加则使用默认比较器,注意这里的有序不是按照添加的顺序。
  • TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。
  • TreeMap 实现了Cloneable接口,它能被克隆。实现了java.io.Serializable接口,它支持序列化。
  • TreeMap基于红黑树实现, containsKey、get、put 和 remove 的时间复杂度是 log(n) 。
  • TreeMap是线程不安全的。 它的iterator 方法返回的迭代器支持fail-fastl机制。

数据结构

TreeMap底层使用红黑树存储节点数据,红黑树本质是一颗二叉查找树,简单介绍一下二叉查找树。

二叉查找树

二叉查找树(Binary Search Tree),也称有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树

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

因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为O(logn).。

但二叉树若退化成了一棵具有n个结点的线性链后,则此些操作最坏情况运行时间为O(n)。后面我们会看到一种基于二叉查找树-红黑树,它通过一些性质使得树相对平衡,使得最终查找、插入、删除的时间复杂度最坏情况下依然为O(logn)。

红黑树

红黑树在二叉查找树的基础上添加了着色和5条相关限制,从而保证了红黑树的增删改查最坏情况下的复杂度为O(logn)。

红黑树的性质:
1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。

入下图所示就是一颗红黑树下图引自(wikipedia:http://t.cn/hgvH1l)

函数源码解析

构造函数

//默认空参,无比较器
public TreeMap() {
   
        comparator = null;
    }
//提供比较器的构造函数
public TreeMap(Comparator<? super K> comparator) {
   
        this.comparator = comparator;
    }
//创建一个包含了map的TreeMap
public TreeMap(Map<? extends K, ? extends V> m) {
   
        comparator = null;
        putAll(m);
    }

左旋/右旋

在红黑树插入或者删除节点后可能会破坏它的性质,通常需要修改着色和左右旋转操作来恢复红黑树的特性。

左旋:

图片来自:https://www.cnblogs.com/skywang12345/p/3310928.html
在这里插入图片描述
如图,将节点X按照X和Y的连线为轴进行左旋,使X成为了Y的左孩子,同时Y的左孩子β,称为了X的右孩子。

步骤大致为:

  1. 将Y的左子节点β设置为X的右子节点
  2. 将X的父节点设置为Y
  3. 将Y的父节点设置成X的父节点,若X的父节点为空则设置Y为根节点。

代码实现:(注释按照上图示例)

	//假设传入的p是上图中的X
    private void rotateLeft(Entry<K,V> p) {
   
        if (p != null) {
   
        	//指针r指向p的右孩子。(X的右孩子Y)
            Entry<K,V> r = p.right;
            //p的右孩子设置为r的左孩子。(X的右孩子设置成β)
            p.right = r.left;
            if (r.left != null){
   
            	//如果r的做孩子不为空,将父节点设置为p。(β父节点设置为X)
                r.left.parent = p;
            }
            //r的父节点设置为p的父节点。(Y的父节点设置为X的父节点)
            r.parent = p.parent;
            if (p.parent == null)
            	//p的父节点为空则表示它的根节点,将r设置成根节点
                root = r;
            else if (p.parent.left == p)
            	//p是父节点的左子节点则将r设置成左子节点
                p.parent.left = r;
            else
            	//同理设置成右子节点
                p.parent.right = r;
            //r的左节点设置成p
            r.left = p;
            //p的父节点设置成r
            p.parent = r;
        }
    }

右旋

图片来自:https://www.cnblogs.com/skywang12345/p/3310928.html
在这里插入图片描述
如图所示,将节点Y按照X和Y的连接线为轴进行右旋,称为了X的 右子节点,同时X的右子节点β称为了Y的左子节点。和左旋差别不大,不做详细介绍

private void rotateRight(Entry<K,V> p) {
   
        if (p != null) {
   
            Entry<K,V> l = p.left;
            p.left = l.right;
            if (l.right != null) l.right.parent = p;
            l.parent = p.parent;
            if (p.parent == null)
                root = l;
            else if (p.parent.right == p)
                p.parent.right = l;
            else p.parent.left = l;
            l.right = p;
            p.parent = l;
        }
    }

put()

向TreeMap中添加节点本质上是在红黑树中添加节点。

    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;
        Comparator<? super K> cpr = comparator;
        //如果有提供比较器
        if (cpr != null) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值