红黑树--java TreeMap

TreeMap红黑树解析 TreeMap小白解析 阅读详情

红黑树是自己看源代码时候最先遇到的比较高级的数据结构,早就想写一篇关于红黑树的文章了,但是因为时间的关系,一直没有弄。。。

首先来说在什么地方用到了红黑树:

(1)nginx的定时事件用到了红黑树,用它来定位当前超时时间最近的节点(libevent采用的是小根堆)

(2)STL中的map底层就是采用红黑树实现的

(3)linux调度中对进程的管理有用到

(4)java的TreeMap采用红黑树实现(HashMap采用散列的方式实现)


上面就是现在我知道的红黑树用到的地方,那么接下来我们来说说红黑树的定义吧:

(1)红黑树是树形结构

(2)它是一颗二叉搜索树(自平衡,但是与AVL树相比,平衡要求没有那么高,但是它的统计查找复杂度与AVL差不多,但是插入删除就简单了一些)


《STL源码剖析》中的定义:

(1)每个几点不是红色就是黑色

(2)根节点是和黑色

(3)如果节点为红,那么它的子节点就必须为黑色

(4)任意节点到树的末尾的任何路径,经过的黑色节点的数目必须相同


着这篇文章中我们以java的TreeMap为例子来说明红黑树的使用

首先来看一下红黑树节点的定义:

    static final class Entry<K,V> implements Map.Entry<K,V> {
        K key;    
        V value;
        Entry<K,V> left = null;  //左子节点
        Entry<K,V> right = null;  //又子节点
        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;
        }

        /**
         * Returns the key.
         *
         * @return the key
         */
        public K getKey() {
            return key;
        }

        /**
         * Returns the value associated with the key.
         *
         * @return the value associated with the key
         */
        public V getValue() {
            return value;
        }

        /**
         * Replaces the value currently associated with the key with the given
         * value.
         *
         * @return the value associated with the key before this method was
         *         called
         */
        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;

            return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
        }

        public int hashCode() {
            int keyHash = (key==null ? 0 : key.hashCode());
            int valueHash = (value==null ? 0 : value.hashCode());
            return keyHash ^ valueHash;
        }

        public String toString() {
            return key + "=" + value;
        }
    }
具体key和value的作用就很明显了吧,另外还有三个引用,分别指向两个子节点和一个父节点,那么画一个图的话,几点就大概长成这个样子:



我们首先从节点的插入说起吧,也就是TreeMap的put方法:

    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;
        // split comparator and comparable paths
        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
                    return t.setValue(value);
            } while (t != null);
        }
        else {
        	//一般情况下key是需要实现ccomparable接口的
            if (key == null)
                throw new NullPointerException();
            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
                    return t.setValue(value);   //如果相等了,那么就直接赋值就好了
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);  //构造新的节点
        if (cmp < 0)
            parent.left = e;  
        else
            parent.right = e;
        fixAfterInsertion(e);  //由于插入了节点,那么有可能导致树不满足红黑树的要求了,所以需要进行一些调整
        size++;
        modCount++;
        return null;
    }

其实就是很普通的二叉搜索树的插入过程,而且从函数的实现可以看出,key类型需要实现comparable接口,或者需要传入comparator才行,不然的话怎么插入啊。。。

另外在前面说到了红黑树的定义,有许多的性质需要满足,因而插入新的节点可能就会导致原来的树不再满足红黑树的性质了,因此需要做一些调整,也就是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;
    }
上述函数就是使得刚刚插入新节点的红黑树通过一些列的旋转操作以及染色操作,使得新的树可以满足红黑树的要求,同时还可以使得树形结构保持相对的平衡,优化查找的时间复杂度。。。另外这里左旋转以及又旋转的动作,这里就以一张图来简单说明一下右旋转吧:


上图就是对节点(X1)进行了又旋转的效果。。。


好了,搞懂节点的插入,那么来看看节点的删除操作吧:

    public V remove(Object key) {
    	//首先找到当前key的节点,其实也就是标准的二叉搜索树的查找过程
        Entry<K,V> p = getEntry(key);
        if (p == null)
            return null;

        V oldValue = p.value;
        deleteEntry(p);  //删除这个节点
        return oldValue;
    }
这个方法的实现还是非常简单的吧,也就是先通过查找找到这个key所对应的节点,查找过程也就是我们以前学数据结构的时候标准的二叉搜索树的查找过程,查找到之后,再调用deleteEntry方法,将这个红黑树节点删去。。。

这里在删除的时候需要注意一个概念,就是在树形结构中删除一个节点时候,总不可能让这里成为一个洞吧,那么就需要在当前的树中找到一个节点来作为替换节点,寻找当前删除节点的替换节点的方法是:

(1)如果当前删除节点有右子结点,也就是说存在又子树,那么又子树的最小的节点就可以成为替换节点

(2)当前删除节点没有又子节点,那么向上寻找,直到找到一个节点是其父亲节点的左儿子为止,或者到了根节点。。。

好了,我们接下来来看看deleteEntry方法定义吧:

  private void deleteEntry(Entry<K,V> p) {
        modCount++;
        size--;

        // If strictly internal, copy successor's element to p and then make p
        // point to successor.
        if (p.left != null && p.right != null) {
            Entry<K,V> s = successor(p);  //获取当前节点的继承节点,并用继承节点来替换当前的节点
            p.key = s.key;
            p.value = s.value;
            p = s;
        } // p has 2 children

        // Start fixup at replacement node, if it exists.
        Entry<K,V> replacement = (p.left != null ? p.left : p.right);  //寻找一个替换节点,用于填充刚刚找到的继承节点

        if (replacement != null) {  //相当于是把刚刚的继承节点的子节点向上提,用于替换刚刚的继承节点
            // Link replacement to parent
            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;

            // Null out links so they are OK to use by fixAfterDeletion.
            p.left = p.right = p.parent = null;  //这里就相当于将继承节点删除了

            // Fix replacement
            if (p.color == BLACK)
                fixAfterDeletion(replacement);  //重新修正红黑树的形状和颜色
        } else if (p.parent == null) { // return if we are the only node.
            root = null;
        } else { //  No children. Use self as phantom replacement and unlink.
            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;
            }
        }
    }

首先找到要删除节点的继承节点,然后用继承的节点的key和value来替换当前删除的节点,然后再将继承节点删除就好了。。最后就还有fixAfterDeletion方法,用于修正删除了节点之后的红黑树的形状和颜色。。


好了,通过对java的TreeMap的一些分析,也算是对红黑树的东西有了一定的了解。。。下一篇看看小根堆吧。。

JAVA Map详解之TreeMap(红黑树) 前言 上一章的HashMap并没有提到红黑树,就是因为本章的TreeMap就是一棵红黑树。TreeMap是存储键值对(key-value结构)的自平衡二叉树,又称红黑树。TreeMap的key是有序且不可为空的,但是value是可以为空的。TreeMap的类图结构如下 TreeMap类上的注释有两个地方需要注意: 1.TreeMap是一个基于NavigableMap实现的红黑树,TreeMap的排序方式有两种:(1)基于key的自然排序(2)在创建TreeMap的时候提供一个比较器,使用哪种排序取决于你使 阅读详情

相关推荐

java8 TreeMap红黑树节点删除源码解析

目录 一、红黑树的规则 二、红黑树节点删除整体分析 三、红黑树节点删除具体情形分析 四、TreeMap删除节点源码解析 红黑树添加节点的平衡操作相对简单,具体分析参考java8 TreeMap接口实现源码解析,红黑树节点删除需要考虑的情形和翻转动作更复杂。综合各博客大神对该问题的分析,以TreeMap中红黑树节点删除相关代码为例,记录下自己对该问题的分析和理解,欢迎指正。 ...

菜鸟进阶之路 1446

JAVA红黑树TreeMap/TreeSet详解

Java中的红黑树是一种自平衡的二叉查找树,它通过特定的旋转和重新着色操作来确保树的高度大致保持在对数级别,从而在平均和最坏情况下都能保证查找、插入、删除等操作的时间复杂度为O(log n)。

m0_74051652的博客 1464

Java TreeMap:基于红黑树的排序映射解析

这种有序性是通过红黑树数据结构来实现的,红黑树是一种自平衡的二叉查找树,它能够在最坏的情况下保证基本的动态集合操作(如查找、插入和删除)的时间复杂度仍然是对数的。因此,“kiwi”和“pear”(长度为4)将出现在“apple”(长度为5)之前,“banana”(长度为6)将出现在最后,即使它是第一个被插入的元素。注意:在上面的输出中,你可能会注意到“orange”和“banana”的顺序似乎与插入顺序不同。存储的键值对默认是按照键的自然顺序进行排序的,也可以根据创建。是一个非常重要的成员,它实现了。

yokeyhui的博客 532

Java 二叉查找树(TreeMap、TreeSet)

二叉查找树(BST)是一种特殊的二叉树,每个顶点最多可以有两个子节点。这种结构遵循 BST 属性,规定给定顶点的左子树中的每个顶点的值必须小于给定顶点的值,右子树中的每个顶点的值必须大于给定顶点的值。二叉查找树支持快速搜索、插入和删除,各种操作的时间复杂度均为 logn,是一种非常常用的数据结构。

沈小洋 513

Java面试要点59 - Java TreeMap源码解析:红黑树实现

TreeMap通过精心设计的红黑树实现,在保证元素有序性的同时,为插入、删除、查找等操作提供了稳定的性能。其中红黑树的平衡机制确保了树的高度维持在对数级别,使得各项操作的时间复杂度保持在O(log n)水平。

程序媛学姐的博客 862

Lumerical INTERCONNECT 中的自相位调制 (SPM) 仿真实践指南

本文详细介绍了在Lumerical INTERCONNECT中进行自相位调制(SPM)仿真的实践指南。从SPM的基础物理原理出发,深入解析非线性薛定谔方程和克尔效应,到INTERCONNECT中NLSE波导元件的配置技巧,以及时域与频域中SPM效应的观测方法。文章还提供了常见问题排查和进阶联合仿真技巧,帮助用户高效完成从仿真到实测的校准过程。

weixin_42565652的博客 187

对JAVA的TreeMap深入理解

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

weixin_61854715的博客 1919

maya中的几个热盒(空格、shift、w键)

maya中许多功能打包放进了不同的热盒中,让我们使用起来更加的便捷,熟练掌握后可以大大提升了建模速度。按下不同按键后鼠标不需要精确点到某个图标,往某个方向滑滑滑就打开了相应功能,非常的炫酷和迅猛。

m0_71983242的博客 1756

Java高阶数据结构之红黑树

本文是Java高阶数据结构的学习,剖析红黑树底层原理,红黑树的时间复杂度,红黑树的插入以及插入时的平衡调整。之后的学习内容将持续更新!!!

qq15035899256的博客 3723

【全网首发】2026华为OD双机位C卷 机考真题题库含考点说明以及在线OJ(OD上机考试双机位C卷)

【全网首发】2026华为OD机位C卷 机考真题题库含考点说明以及在线OJ(OD上机考试双机位C卷)

算法大师! 8748

最详细的 SAP ABAP Web Service 创建和消费步骤讲解

本文原版发表于 SAP 官方社区,当时用的英文版写作,标题为:Step by step to create, consume and trace web service in ABAP system https://blogs.sap.com/2014/05/20/step-by-step-to-create-consume-and-trace-web-service-in-abap-system/ 总共阅读量超过 18 万。 How to create web service provider in A

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

java map 红黑树_Java 中TreeMap详解和红黑树

Java的一些组件很有趣,希望大家多深入研究研究!A. 核心要点:类继承public class TreeMapextends AbstractMapimplements NavigableMap, Cloneable, Serializable一个有序的key-value集合,基于红黑树(Red-Black tree)实现。该映射根据其键的自然排序进行排序,或者根据创建映射时提供的 Compar...

weixin_39945178的博客 272

合肥工业大学操作系统实验9

依据操作系统课程所介绍的页面置换算法与动态内存分配的原理,本实验的目的是掌握OPT、FIFO、LRU、LFU、Clock等页面置换算法;掌握可用空间表及分配方法;掌握边界标识法以及伙伴系统的内存分配方法和回收方法。从而深入理解操作系统的内存管理。从而加深对教材上页面置换算法与动态内存分配的理解。

Martin-share的博客 1094

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

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

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

单片机:实现FFT快速傅里叶变换算法(附完整源码)

单片机:实现FFT快速傅里叶变换算法(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1098

Qt Quick/QML入门到精通_专栏demo对应文章目录(目前50个左右demo/长期更新)

自定义图标下拉列表框的效果如下所示:自定义三态按钮的效果如下所示:传送门:53.qt quick-自定义三态按钮、自定义图标下拉列表框_诺谦的博客-优快云博客传送门:59.qt quick-qml调色板(颜色取色器)实现、自定义QQuickStyle样式_诺谦的博客-优快云博客63.qt quick-QML侧边滑动栏(不需要任何图片资源,支持自定义左右方向和大小)_诺谦的博客-优快云博客_qt实现好看侧边栏。...

抵抗时间扭曲,坚持做一件有意义的事 2万+

java treeset 红黑树_红黑树和TreeSet与TreeMap

1. Treeset我们都知道,是一个有序的集合。但是如果分析TreeSet的实现源码,会发现构造方法中存在 TreeMap。换句话说:TreeSet的底层就是通过TreeMap来实现2.TreeMapTreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点。向TreeMap中每次Put一对Entry时,实际上是给红黑树中添加一个节点。这种方...

weixin_33138569的博客 596

华为OD机试双机位C卷-分披萨 (Py/Java/C/C++/Js/Go)

华为OD机试(机考)双机位C卷 【分披萨】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为od机试 - 分披萨。

qq_45776114的博客 1396

Java集合详解6:TreeMap和红黑树

Java集合详解6:TreeMap和红黑树 初识TreeMap 之前的文章讲解了两种Map,分别是HashMap与LinkedHashMap,它们保证了以O(1)的时间复杂度进行增、删、改、查,从存储角度考虑,这两种数据结构是非常优秀的。另外,LinkedHashMap还额外地保证了Map的遍历顺序可以与put顺序一致,解决了HashMap本身无序的问题。 尽管如此,HashMap与Link...

weixin_34290631的博客 263

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Java 基于红黑树的TreeMap,TreeSet实现原理

TreeSet and TreeMap 总体介绍 之所以把TreeSet和TreeMap放在一起讲解,是因为二者在Java里有着相同的实现,前者仅仅是对后者做了一层包装,也就是说TreeSet里面有一个TreeMap(适配器模式)**。因此本文将重点分析TreeMap。 Java TreeMap实现了Sort...

无极侠岚的博客 264

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

JAVA TreeMap 红黑树

通过分析 JDK 源代码研究 TreeMap 红黑树算法实现 TreeMap 和 TreeSet 是 Java Collection Framework 的两个重要成员,其中 TreeMap 是 Map 接口的常用实现类,而 TreeSet 是 Set 接口的常用实现类。虽然 HashMap 和 HashSet 实现的接口规范不同,但 TreeSet 底层是通过 TreeMap

847

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

【Java基础】HashMap、TreeMap、都用了红黑树

引子 昨天模拟面试,面试官问到了 哈希map 和 treeMap 我说都是使用了 红黑树 问我有什么区别 还有复杂度 稍微一深入讨论 我就废掉了 先亡羊补牢一下 文章目录引子1)、使用层次上的区别:HashMap:TreeMap:2)、底层数据结构HashMap:HashTree:总结:红黑树特征:红黑树左旋、右旋: 1)、使用层次上的区别: HashMap: 数组+链表存储key-value,1.8加入红黑树(优化链表查找过长的问题) 允许null作为key和value,key不可以重复,value允许

韩旭051的博客 915

大话红黑树之(2)源码讲解TreeMap-Java

TreeMap是 Java 中基于红黑树实现的有序Map,它能够保证键的自然顺序或者根据指定的比较器顺序。通过红黑树的数据结构,TreeMap保证了操作的时间复杂度为 O(log n)。下面将从源码角You:TreeMap是 Java 中基于红黑树实现的有序Map,其核心特性是能保证键的自然顺序,或者根据自定义比较器提供的顺序。通过红黑树的数据结构,TreeMap保证了常规操作(如插入、删除和查找)的时间复杂度为O(log n)。接下来,我们将从源码的角度详细分析TreeMap。

执剑人 907

结合 TreeMap 源码分析红黑树在 java 中的实现

注:本文的源码摘自 jdk1.8 中 TreeMap 红黑树的意义 红黑树的性质 左旋、右旋 增 删 总结 红黑树的意义红黑树本质上是一种特殊的二叉查找树,红黑树保证了一种平衡,插入、删除、查找的最坏时间复杂度都为 O(logn)。那么红黑树是如何实现这个特性的呢?红黑树区别于其他二叉查找树的规则在于它的每个结点拥有红色或黑色中的一种颜色,然后按照一定的规则组成红黑树,而这个规则就是我们这篇文章所想

joker 1339

java进阶---红黑树在java中的应用:treemap

原文出自---http://blog.youkuaiyun.com/chenssy/article/details/26668941 本身treemap相对于其他类型的map用的是比较少的,最近在工程中发现在工程中,使用treemap作为一个集合,存储一个注解里面的对象。最后在使用的过程中,可以通过这个集合找到使用注解的方法,最后找到对应的信息,所以赶紧深入了解了一下treemap的深...

anys0039的博客 717

JAVA之TreeMap与红黑树详解

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

BehandTheTime的博客 1077
上一篇: java netty之ByteToMessageDecoder
下一篇: java netty之MessageToByteEncoder
fjs_cloud
博客等级 码龄15年 248粉丝 192原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值