TreeMap源码分析与红黑树实现原理

144面试必问:你能说出 TreeMap 原理实现及常用方法吗? 更多面试题:面试题内容聚合 目录 TreeMap概述 红黑树回顾 TreeMap构造 put方法 get 方法 remove方法 遍历 总结 一. TreeMap概述 TreeMap存储K-V键值对,通过红黑树(R-B tree)实现; TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接... 阅读详情

1. 引言

TreeMap是Java集合框架中的一个重要成员,基于红黑树(Red-Black Tree)实现,能够保证键值对按照键的顺序排列。本文将深入分析TreeMap 的源码,本文采用的JDK版本是21,重点介绍其数据结构、核心操作以及红黑树的实现原理。

2. 继承关系分析

TreeMap 继承自 AbstractMap<K, V>,并实现了 NavigableMap<K, V> 和 SortedMap<K, V> 接口。

该类的继承结构图如下图所示:

3. TreeMap的数据结构

3.1 TreeMap的核心成员变量

private transient Entry<K,V> root;  // 根节点
private transient int size = 0;      // TreeMap 的大小
private final Comparator<? super K> comparator; // 比较器(可选)

3.2 Entry<K,V>节点结构

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 = RED; // 新插入的节点默认是红色
}

其中:

  • key/value:键值对
  • left/right:左右子节点
  • parent:父节点
  • color:节点颜色(红/黑)4. 红黑树的基本概念
  • 红黑树是一种 自平衡二叉搜索树(BST),具备以下性质:
  • 每个节点非红即黑。
  • 根节点始终是黑色。
  • 红色节点的子节点必须是黑色(即不能出现两个连续的红色节点)。
  • 从任一节点到叶子节点的所有路径,黑色节点数必须相同(黑色平衡性)。
  • 新插入的节点默认是红色(避免破坏黑色平衡)。

4.1 红黑树的操作

红黑树的三种操作:变色、左旋、右旋。

  • 变色:
  • 节点的颜色由红变黑或由黑变红。(这个操作很好了解)
  • 左旋:
  • 以某个结点作为支点(pivot),其父节点(子树的root)旋转为自己的左子树(左旋),pivot的原左子树变成原root节点的右子树,pivot的原右子树保持不变,我这里有张图方便理解
  • 从这张图可以看出,以18这个元素为pivot,把其父节点9旋转为自己的左子树,pivot的原左子树10变成root节点9的右子树
  • 右旋:
  • 以某个结点作为支点(pivot),其父节点(子树的root)旋转为自己的右子树(右旋),pivot的原右子树变成 原root节点的左子树,pivot的原左子树保持不变,我这里有张图方便理解
  • 从这张图可以看出,以7这个元素为pivot,把其父节点9旋转为自己的右子树,pivot的原右子树8变成root节点9的左子树

4.2 红黑树插入场景旋转分析

实际应用插入的场景中,红黑树的旋转情况非常多,下面我一一例举几种场景,为源码分析做准备

  • 场景1: 红黑树为空树

直接把插入结点作为根节点就可以了并且把插入节点设置为黑色。

  • 场景2: 插入节点的父节点为黑色 由于插入的节点是红色的,当插入节点的父节点是黑色时,不会影响红黑树的平衡,如下图所示:

  • 场景3: 插入节点的父节点为红色

如果插入节点的父节点为红色节点,由于新插入得节点也为红色,根据规则不能有两个相邻得红色节点,此时分两种情况考虑

  • 父亲和叔叔均为红色
  • 父亲为红色,叔叔为黑色

如图(K为要插入的元素):

场景3.1:父亲和叔叔为红色节点

父亲为红色,那么此时该插入子树的红黑树层数的情况是:黑红红。

因为不可能同时存在两个相连的红色节点,需要进行变色,显然处理方式是把其改为:红黑红。

变色处理: 黑红红 ==> 红黑红

1.将F和V节点改为黑色

2.将P改为红色

3.将P设置为当前节点,进行后续处理

此时操作如下图:

场景3.2:叔叔为黑色,父亲为红色,并且插在父亲的左节点

分为两种情况

  • LL红色插入

叔叔为黑色,或者不存在(NIL)也是黑节点,并且节点的父亲节点是祖父节点的左子节点

如图:

场景3.2.1 LL型失衡

细分场景 1: 新插入节点,为其父节点的左子节点(LL红色情况),插入后就是LL型失衡,如图所示:

自平衡处理:

1.变颜色:

将F设置为黑色,将P设置为红色

2.对F节点进行右旋

如图:

场景3.2.2 LR型失衡

细分场景2:新插入节点,为其父节点的右子节点(LR红色情况),插入后就是LR型失衡,如图所示:

自平衡处理:

1.对F进行左旋

2.将F设置为当前节点,得到LL红色情况

3.按照LL红色情况处理(1.变色 2.右旋P节点)

场景3.3:叔叔为黑节点,父亲为红色,并且父亲节点是祖父节点的右子节点

场景3.3.1:RR型失衡

新插入节点,为其父节点的右子节点(RR红色情况)

自平衡处理:

1.变色:

将F设置为黑色,将P设置为红色

2.对P节点进行左旋

场景3.3.2:RL型失衡

新插入节点,为其父节点的左子节点(RL红色情况)

自平衡处理:

1.对F进行右旋

2.将F设置为当前节点,得到RR红色情况

3.按照RR红色情况处理(1.变色 2.左旋 P节点)

5. TreeMap源码分析

5.1 插入(put方法)

该方法的签名如下:

private V put(K key, V value, boolean replaceOld) {
        Entry<K,V> t = root;
        if (t == null) {
            addEntryToEmptyMap(key, value);
            return null;
        }
        int cmp;
        Entry<K,V> parent;

        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 {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        } else {
            Objects.requireNonNull(key);
            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 {
                    V oldValue = t.value;
                    if (replaceOld || oldValue == null) {
                        t.value = value;
                    }
                    return oldValue;
                }
            } while (t != null);
        }
        addEntry(key, value, parent, cmp < 0);
        return null;
}

首先分析第一次添加的时候,比如添加的是元素3,把root成员属性赋值给变量t,第一次t是null,调用addEntryToEmptyMap方法,该方法的签名如下:

private void addEntryToEmptyMap(K key, V value) {
        compare(key, key); 
        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
}

主要是创建一个Entry类型的root对象,设置size等于1,modCount加1,最终返回null,首次添加的逻辑很简单。

接着分析第二次添加的时候,比如添加的是元素2,定义int类型变量cmp,Entry类型的parent,把成员变量类型是Comparator的comparator赋值给cpr, 判断它是否为null,这块的comparator可以由调用端自行传入,假如没传递这个,会走到else语句中,把key强制转换为Comparable类型,从这块可以看出,如果是 自定义类型的key,需要实现Comparable接口,否则会报ClassCastException异常,相信这里大家都理解,这时候有do while循环,把元素t也是root赋值给变量parent, 用当前插入元素和根元素比较, 如果小于根元素说明是根元素的左子树,把根元素的左子树赋值给变量t,判断t是否为空,如果不为空继续判断直到null为止,经过这一系列do while循环比较就能找出 新传入的元素父亲节点parent,如果比较以后相等说明就是要覆盖之前的老值,然后返回老值。

找到要插入元素的父亲节点以后,调用addEntry方法,该方法的签名如下;

  private void addEntry(K key, V value, Entry<K, V> parent, boolean addToLeft) {
      Entry<K,V> e = new Entry<>(key, value, parent);
      if (addToLeft)
          parent.left = e;
      else
          parent.right = e;
      fixAfterInsertion(e);
      size++;
      modCount++;
  }

创建Entry对象,传入key、value、父亲节点,如果要插入到左子树把当前插入的元素赋值给parent.left,如果是右子树把当前插入的元素赋值给parent.right,插入以后需要进行自平衡操作,调用方法 fixAfterInsertion,该方法的签名如下:

private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
}

判断要插入的元素是不是根节点,如果是根节点更新颜色为黑色,否则是红色,这里我们重点关注是红色的逻辑,先是一个while的死循环,结束条件是插入的元素 不为空并且不是root节点并且是插入元素父节点是红色。在while循环有两个大的分支判断。

  • x的父节点是其祖父节点的左子节点:
    获取变量y,y是祖父节点的右子节点,即是x的叔叔节点,这里又分两种情况:
    • 如果叔叔也是红色节点,根据第四章节分析的场景3.1父亲和叔叔为红色节点情况可知:
      将叔叔节点和父节点设置为黑色节点,设置祖父节点为红色,祖父节点设置为当前节点,进行后续处理继续进行修复。
    • 如果叔叔节点y是黑色的,进入下一个修复步骤:
  • 如果x是父节点的右子节点,就是第四章节分析的场景3.2.2LR型失衡,首先得到节点x的父节点并重新赋值给x,将父节点x 进行左旋操作,调用的方法是rotateLeft,该方法的签名如下:
  • private void rotateLeft(Entry<K,V> p) { if (p != null) { Entry<K,V> r = p.right; p.right = r.left; if (r.left != null) r.left.parent = p; r.parent = p.parent; if (p.parent == null) root = r; else if (p.parent.left == p) p.parent.left = r; else p.parent.right = r; r.left = p; p.parent = r; } }
  • 那我按照场景3.2.2图场景分析,获取p.right的代表变量r等于节点K,将K的左子树挂到p的右子树上,因为p要下沉成为K的左子树,设置K的左子树的父节点是F, K的父指针指向F的原父节点,这样有助于K成为F的父节点,判断p.parent如果等于null,说明p是根节点,那么k成为新的根节点,否则更新F父节点的左子树为K 或者更新F父节点的右子树为K,最后将K的左子树变为F,F的父子树变为K,通过rotateLeft方法就把原先的父节点变为当前节点的子节点,但是还没有完事,因为此时相邻 节点还都是红色的,需要将节点P变红,K变黑,然后进行右旋平衡操作,调用的方法的是rotateRight,该方法的签名是:
  • 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; } }
  • 此时传入的节点是P,把P的左子树赋值给变量l,把l的右子树赋值给P的左子树,判断如果l的右子树不为空,把l的右子树的parent设置为P,设置元素k的parent指向 P的parent,判断p.parent如果等于null,说明P是根节点,那么K成为新的根节点,否则更新P父节点的左子树为K或者右子树,最后将P变成K的右子树,P的父节点变成K。
  • x的父节点是其祖父节点的右子节点:

这块的逻辑其实是跟上面的逻辑是对称操作,是上面的相反方向的操作,这里我就不分析了,感兴趣的同学可以自行分析一下。

5.2 查找(get方法)

该方法的签名如下:

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

final Entry<K,V> getEntry(Object key) {
        if (comparator != null)
           return getEntryUsingComparator(key);
        Objects.requireNonNull(key);
        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;
}

get方法逻辑比较简单,首先获取根元素赋值给变量p,然后根据传入的元素调用compareTo方法进行比较,根据结果和0比较,如果小于就把p的左子树赋值给p,如果大于就把 p的右子树赋值给p,如果等于直接返回元素p。

5.3 forEach方法

该方法的签名如下:

 public void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        int expectedModCount = modCount;
        for (Entry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
            action.accept(e.key, e.value);

            if (expectedModCount != modCount) {
                throw new ConcurrentModificationException();
            }
        }
}

此次是一个for循环,可以看出先调用了getFirstEntry方法获取节点元素,那么再来看看这个方法的签名:

final Entry<K,V> getFirstEntry() {
    Entry<K,V> p = root;
    if (p != null)
        while (p.left != null)
            p = p.left;
    return p;
}

把根节点赋值给变量p,接着while一个死循环,判断如果p的左子树不为空,就把左子树赋值给p,再次循环判断直到p的左子树为空然后返回p,这里为什么要这么 做那,因为TreeMap是按照key值有序排列的,它的底层是一个红黑树,左子树的值比根节点值小,根节点值又小于右子树节点,这样一直循环获取的就是最小的左子树值,最小值 可以先取出来。

接着返回到forEach的for循环,调用getFirstEntry如果返回的不为空,就打印key,value值。

接着调用了successor方法传入getFirstEntry方法返回的节点e,该方法主要获取节点e的后继节点,该方法的签名如下:

static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
      if (t == null)
          return null;
      else if (t.right != null) {
          Entry<K,V> p = t.right;
          while (p.left != null)
              p = p.left;
          return p;
      } else {
          Entry<K,V> p = t.parent;
          Entry<K,V> ch = t;
          while (p != null && ch == p.right) {
              ch = p;
              p = p.parent;
          }
          return p;
      }
}

判断当前节点是否有右子树,如果有就一直while循环获取右子树中最左边节点,也就是右子树中最小的那个节点,这种是树的中序遍历形式,访问顺序是:左子树 -> 当前节点 -> 右子树。

如果当前节点没有右子树,需要向上查找后继节点。此时的后继节点是第一个使当前节点位于其左子树中的祖先节点,为什么要这么查找那?因为在中序遍历中当前节点是左子节点,那么父节点就是它的后继(因为父节点还没被访问)。

5.4 remove方法

该方法的签名如下:

 public V remove(Object key) {
        Entry<K,V> p = getEntry(key);
        if (p == null)
            return null;

        V oldValue = p.value;
        deleteEntry(p);
        return oldValue;
}

首先调用getEntry方法获取要删除的节点元素,如果获取不到返回null,如果获取到了先保留Entry的value值,然后调用deleteEntry方法,该方法签名如下:

private void deleteEntry(Entry<K,V> p) {
    modCount++;
    size--;
    if (p.left != null && p.right != null) {
        Entry<K,V> s = successor(p);
        p.key = s.key;
        p.value = s.value;
        p = s;
    }
    Entry<K,V> replacement = (p.left != null ? p.left : p.right);
    
    if (replacement != null) {
        replacement.parent = p.parent;
        if (p.parent == null)
            root = replacement;
        else if (p == p.parent.left)
            p.parent.left  = replacement;
        else
            p.parent.right = replacement;
        
        p.left = p.right = p.parent = null;
        
        if (p.color == BLACK)
            fixAfterDeletion(replacement);
    } else if (p.parent == null) {
        root = null;
    } else {
        if (p.color == BLACK)
            fixAfterDeletion(p);

        if (p.parent != null) {
            if (p == p.parent.left)
                p.parent.left = null;
            else if (p == p.parent.right)
                p.parent.right = null;
            p.parent = null;
        }
    }
}

判断要删除(p)的节点是否同时存在左右节点,如果是找出后继节点,这个后继方法我已经在forEach方法中已经分析过了,把p的key、value替换为后继节点的key、value,最后把p指向后继节点。

下面的操作就是要删除找到的后继节点,在删除之前要判断它是否有左子树或者右子树:

  • 如果有

把replacement的父节点设置为p=s的父节点,如果p=s的父节点为null,说明p=s是父亲节点,那么 删除了后继节点,自然root节点变为replacement,否则就进行判断是把replacement放在p=s的左边还是右边,然后p.left = p.right = p.parent = null把指向都为null,方便删除p=s的元素 这个操作完成以后就删除了后继节点,而后继节点的key,value被删除的后继节点的key,value覆盖了,看的像是要删除remove中的元素,其实是覆盖,真实删除的是后继节点。

接着判断删除的元素是否是黑色的,如果是黑色的,需要进行树的平衡操作,这是为啥那,因为根据红黑树的特性,从任意节点到其所有叶子节点的路径上,黑色节点的数量相同(称为“黑高”平衡),所以删除元素 以后需要平衡操作,下面看一下fixAfterDeletion方法签名:

private void fixAfterDeletion(Entry<K,V> x) {
        while (x != root && colorOf(x) == BLACK) {
            if (x == leftOf(parentOf(x))) {
                Entry<K,V> sib = rightOf(parentOf(x));

                if (colorOf(sib) == RED) {
                    setColor(sib, BLACK);
                    setColor(parentOf(x), RED);
                    rotateLeft(parentOf(x));
                    sib = rightOf(parentOf(x));
                }

                if (colorOf(leftOf(sib))  == BLACK &&
                    colorOf(rightOf(sib)) == BLACK) {
                    setColor(sib, RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(rightOf(sib)) == BLACK) {
                        setColor(leftOf(sib), BLACK);
                        setColor(sib, RED);
                        rotateRight(sib);
                        sib = rightOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), BLACK);
                    setColor(rightOf(sib), BLACK);
                    rotateLeft(parentOf(x));
                    x = root;
                }
            } else { 
                Entry<K,V> sib = leftOf(parentOf(x));

                if (colorOf(sib) == RED) {
                    setColor(sib, BLACK);
                    setColor(parentOf(x), RED);
                    rotateRight(parentOf(x));
                    sib = leftOf(parentOf(x));
                }

                if (colorOf(rightOf(sib)) == BLACK &&
                    colorOf(leftOf(sib)) == BLACK) {
                    setColor(sib, RED);
                    x = parentOf(x);
                } else {
                    if (colorOf(leftOf(sib)) == BLACK) {
                        setColor(rightOf(sib), BLACK);
                        setColor(sib, RED);
                        rotateLeft(sib);
                        sib = leftOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), BLACK);
                    setColor(leftOf(sib), BLACK);
                    rotateRight(parentOf(x));
                    x = root;
                }
            }
        }

        setColor(x, BLACK);
}

这块变色和旋转逻辑和新增时候差不多就不再赘述了。

  • 如果没有如且它的parent为null

把root设置为null,说明树里面一个元素都没有了。

  • 如果不是上面的任意情况

就删除这个后继元素

6 总结

TreeMap通过红黑树保证数据的有序性,同时提供 O(log n) 级别的查找、插入和删除操作。掌握红黑树的旋转(左旋、右旋)及平衡调整机制,可以更好地理解TreeMap的内部实现!

高速信号设计中的关键测试项目与测试仪器详解 高速信号设计是一个多维度协同优化的系统工程。时域分析如同外科手术刀般精准定位物理缺陷,频域分析提供传输通道的频谱指纹,信号质量评估量化系统鲁棒性,接收端容限测试验证极端工况下的可靠性,协议一致性确保生态互操作性,而电源完整性则直指诸多信号问题的根源。 阅读详情

相关推荐

DIY音频必备:手把手教你用AB类功放模块打造高保真桌面音响(含电路图)

本文详细指导如何利用AB类功放模块DIY高保真桌面音响,涵盖选型指南、核心元器件清单、电路搭建与焊接工艺,以及系统调试与音质优化技巧。AB类功放以其接近A类功放的音质和B类功放的效率,成为音频爱好者的理想选择,适合有一定电子基础的实践者。

weixin_42665725的博客 444

一遍文章了解Java集合框架TreeMap实现原理

分析Java TreeMap源码 剖析其实现原理,并自定义实现其功能

qq_42596789的博客 785

最优化方法 25:PDHG

前面的章节要么从原始问题出发,要么从对偶问题出发,通过求解近似点或者一个子优化问题进行迭代,而且推导过程中我们发现根据问题的参数特征,比如矩阵 AAA 是瘦高型的还是矮胖型的,采用对偶和原始问题的复杂度会不一样,可以选择一个更简单的。而这一节,我们将要从原始对偶问题出发来优化,什么是原始对偶问题呢?就是原始优化变量和对偶优化变量(原始函数和共轭函数)混合在一块,看下面的原理就知道了。 1. 原始对偶问题 现在考虑原始优化问题,其中 f,gf,gf,g 为闭凸函数 min⁡f(x)+g(Ax) \min \q

Glooow的博客 4601

TreeMap 原理实现及常用方法

TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现;TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;

z_ssyy的博客 2148

Python实现ARIMA模型

以下是一个使用Python实现ARIMA(自回归移动平均模型)的简单示例代码。在运行代码之前,请确保已经安装了pandasnumpy和库。ARIMAorder(2,0,2)p = 2d = 0q = 2forecastmatplotlib在实际应用中,你需要将模拟数据替换为真实的时间序列数据,并且通过合适的方法(如信息准则等)来确定pdq的最佳值。除了auto_arima在使用pmdarima。

huanghm88的专栏 1736

深入解析BLheli电调固件与Dshot通讯协议:从原理到实践

本文深入解析了无人机动力系统的核心——BLheli电调固件与Dshot通讯协议。详细阐述了BLheli固件从经典版到BLheli_32的演进及其功能特性,并重点剖析了Dshot数字协议如何通过数据包和校验码实现更快、更准、更可靠的电机控制。文章还涵盖了双向Dshot遥测、实战配置与调参指南,帮助玩家充分释放无人机性能。

weixin_28273593的博客 436

TreeMap源码解析

文章目录1 TreeMap1.1 二叉查询树、红黑树1.2 TreeMap的底层实现 1 TreeMap TreeMap是基于红黑树结构实现的一种Map,要分析TreeMap的实现首先就要对红黑树有所了解。 要了解什么是红黑树,就要了解它的存在主要是为了解决什么问题,对比其他数据结构比如数组,链表,Hash表等树这种结构又有什么优点。 1.1 二叉查询树、红黑树 简单总结一下数组,链表,Hash表以及树的优缺点: 数组 优点:(1)随机访问效率高(根据下标查询),(2)搜索效率较高(可使用折半方法)

上善若泪 833

目标检测: yolo26在RK3588上部署

YOLO26发布:端到端目标检测新突破 摘要:Ultralytics最新发布的YOLO26算法采用原生端到端设计,无需NMS后处理,显著提升推理效率。该模型基于三大核心原则: 简洁性:消除NMS步骤,实现直接预测 部署效率:简化集成流程,降低延迟 训练创新:引入MuSGD混合优化器,加速收敛 部署教程显示,YOLO26可适配RK3588等边缘设备,通过ONNX转换和RKNN工具链实现高效部署。其创新架构特别优化了分割、姿态估计等专业任务,标志着目标检测技术的重要革新。 (字数:149)

qianxun 1448

对JAVA的TreeMap深入理解

今天我们来分析TreeMap的底层原理,深入了解TreeMap底层是如何实现的。在分析源码之前我们先来分析红黑树这种数据结构,这样我们呢才能看明白TreeMap的底层原理。 在讲红黑树之间我们先来讲一下二叉搜索树,因为红黑树就是二叉搜索树的一种形式。二叉搜索树就是该二叉树的每一个节点都大于它左子树的所有节点,小于它所有右子树的节点,画一个图来说明。 我们可已发现6大于它所有左子树的节点,小于它所有右子树的节点,且左右子树的节点依旧保持这个性质,这就是二叉搜索树。 我们继续来讲红黑树,首先先说明几个

weixin_61854715的博客 1921

斑鬣狗优化算法(SHO)详解:从生物原理到代码实战

斑鬣狗优化算法(SHO)是一种新型元启发式优化算法,灵感来源于斑鬣狗的群体狩猎行为。该算法通过模拟斑鬣狗的包围猎物、协同狩猎和攻击猎物等生物行为,将优化问题转化为智能搜索过程。SHO算法包含探索与开发两个阶段,通过数学模型实现群体协作和自适应平衡,具有参数简单、收敛快等优势。文章系统介绍了SHO的生物基础、数学模型、实现方法(包括MATLAB和Python代码)、改进策略以及在函数优化、路径规划等领域的应用案例,最后讨论了算法的优势、局限性和未来研究方向。

qq_42212808的博客 237

管好 SAP Fiori Launchpad Content:技术目录、业务目录与 KPI 工具的完整治理思路

SAP Fiori Launchpad 作为企业级统一入口,需通过技术目录(源对象)和业务目录(引用编排)分层治理。技术目录由 Launchpad App Manager 维护,负责应用描述对象(Tile 和 Target Mapping)的创建与管理;业务目录由 Launchpad Content Manager 编排,通过引用技术目录对象实现灵活配置。分析型内容则通过 F2814 统一管理。这种分层设计支持应用复用,便于升级维护,是 SAP S/4HANA 项目中实现高效内容治理的关键。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 132

Java基础 之 TreeMap源码分析

  最开始我直接去看的源码,发现一个问题,虽然我知道红黑树是什么,但是我对红黑树的一些概念甚是模糊。所以再了解源码之前,还是要先了解一下什么是红黑树。 红黑树 概念 红黑树是二叉查找树的一种,再二叉树的基础上加入了color,所以也叫R-B tree。数据结构如图: 红黑树的几个基本原则: 1,每个节点都有一种颜色,非黑即红。 2,根节点必须是黑色。 3,每个叶节点或空节点必须...

maybe的博客 232

Unity引擎开发:复杂动作系统_动作捕捉数据的应用与整合

通过以上步骤,我们可以在Unity中有效地应用和整合动作捕捉数据,以实现更加真实和流畅的动画效果。从导入文件格式的选择,到预处理和优化,再到实时应用和调试验证,每一步都至关重要。希望本文能帮助你在Unity项目中更好地利用动作捕捉数据,提升动画质量和性能。

chenlz2007的博客 958

Java面试-TreeMap 的底层实现:红黑树详解

《Java面试200问:TreeMap红黑树底层实现详解》 本文深入剖析了TreeMap基于红黑树的有序映射实现机制。主要内容包括: 红黑树的五大特性:节点双色性、根黑规则、叶黑规则、红节点限制和黑高一致性,这些特性保证了树的高度平衡(h≤2log(n+1))。 关键操作原理: 插入时通过染色和旋转(左旋/右旋)处理四种冲突场景 删除时通过复杂调整维护黑高平衡 TreeMap源码实现: 使用Entry节点维护红黑树结构 put/get/remove操作均保持O(log n)时间复杂度 原生支持高效的范围查询

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

TreeMap原理实现及常用方法

目录 一. TreeMap概述 二. 红黑树回顾 三. TreeMap构造 四. put方法 五. get 方法 六. remove方法 七. 遍历 八. 总结 前面我们分别讲了Map接口的两个实现类Ha...

weixin_30763455的博客 120

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Java集合系列源码分析(八)-- TreeMap(红黑树)

一、TreeMap TreeMap是有序的,可以实现自定义的比较器来决定升序还是降序。存储key-value,内部使用红黑树。 强烈建议先看之前的二叉搜索树和平衡二叉树,了解二叉搜索树和平衡二叉树再看这篇,否则红黑树有点难理解 二、TreeMap常用方法 2.1 属性 实现了NavigableMap接口, 而NavigableMap又继承于SortedMap,又此可知,这个map是有序的。因...

Double____C的博客 376

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

方法实现原理_TreeMap原理实现及常用方法

一. TreeMap概述TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现;TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;Tree...

weixin_34541572的博客 162

java集合框架源码解析之TreeMap源码解析

asdrgfvewargerw

qq_37520037的博客 355

TreeMap 底层原理

目录 一:TreeMap的基本概念: 二:源码解析 一:TreeMap的基本概念: TreeMap集合是基于红黑树(Red-Black tree)的 NavigableMap实现。该集合最重要的特点就是可排序,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。这句话是什么意思呢?就是说TreeMap可以对添加进来的元素进行...

qq_33419463的博客 1696

TreeMap是如何实现去重和排序的

TreeMap实现了SortedMap接口,它是一个key有序的Map类。 TreeMap的默认排序规则 TreeMap并不是根据插入的顺序来排序,而是根据key元素的 compareTo 方法来排序。 测试代码: Map<Integer, Object> map = new TreeMap<>(); map.put(5, new Object()); map.put(10, new Object()); map.put(8, new Object()); map.put(12, n

czx2018的博客 6535

Java TreeMap原理实现及常用方法

1、TreeMap概述:原文链接 TreeMap存储K-V键值对,通过红黑树(R-B tree)实现; TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现; TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化; TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;

单枪匹码 633

Kafka延迟队列三种实现方案对比与选型指南

消息队列是分布式系统解耦与异步化的核心组件,而延迟消息普遍存在于订单超时关闭、定时任务重试等场景。Kafka作为高吞吐消息中间件,原生并不支持延迟投递,如何在不引入额外中间件的前提下实现可靠延迟队列成为工程实践难点。文章从延迟队列基本原理出发,对比时间戳扫描、时间轮、多层Topic+Redis三类方案,分析各自在延迟精度、可靠性、吞吐能力上的差异,并结合Kafka消费机制与运维监控给出选型建议。针对消息堆积、重复消费、乱序等典型问题提供排查思路,为构建高可用的延迟消息链路提供参考。

weixin_33842304的博客 432

Java集合之TreeMap源码分析

一、概述   TreeMap是基于红黑树实现的。由于TreeMap实现了java.util.sortMap接口,集合中的映射关系是具有一定顺序的,该映射根据其键的自然顺序进行排序或者根据创建映射时提供的Comparator进行排序,具体取决于使用的构造方法。另外TreeMap中不允许键对象是null。   1、什么是红黑树?   红黑树是一种特殊的二叉排序树,主要有以下几条基本性质...

dengduoya4019的博客 91

TreeMap

TreeMap的实现是红黑树算法的实现,所以要了解TreeMap就必须对红黑树有一定的了解,其实这篇博文的名字叫做:根据红黑树的算法来分析TreeMap的实现,但是为了与Java提高篇系列博文保持一致还是叫做TreeMap比较好。通过这篇博文你可以获得如下知识点:        1、红黑树的基本概念。        2、红黑树增加节点、删除节点的实现过程。        3、红黑

Summer_YuXia的博客 367

容器源码分析之TreeMap(十)

1.TreeMap的结构 我们知道,TreeMap的内部实现就是一个红黑树。 关于红黑树的介绍,建议大家先看看这里 红黑树的原理和算法详细介绍 2.TreeMap的继承关系 - 3.TreeMap的成员变量 public class TreeMapK,V> extends AbstractMapK,V> implements NavigableMapK,V>, Clone

heihei 332

搞懂Java集合框架之TreeMap

搞懂Java集合框架之TreeMap简述红黑树源码分析1、TreeMap的基本框架2、get方法3、put方法4、remove方法小结 简述 ​  在Map集合框架中,除了HashMap以外,我们比较常用的还有TreeMap。HashMap是无序的,而TreeMap底层由红黑树实现,可以根据对应的排序规则对key进行排序。 TreeMap的继承关系图 红黑树 概述 红黑树是一种自平衡的二叉查找树 红黑树的每一个节点要么是红色,要么是黑色 红黑树是一种弱平衡的二叉树,它的平衡是由“红黑树的特性”实

qq_41743796的博客 595
上一篇: 数据仓库、数据湖、湖仓一体都是什么意思,究竟有什么区别?
下一篇: 一文读懂 MCP:让 AI 更聪明、更实用的“魔法协议”
程序员职业指南
博客等级 码龄4年 2136粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值