跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 红黑树原理与实现:变色旋转及完整代码

红黑树是一种自平衡二叉搜索树,通过颜色约束确保最长路径不超过最短路径的两倍。核心内容包括红黑树的四条规则,插入操作的三种处理情况:变色、单旋加变色、双旋加变色。提供完整的 C++ 代码实现,涵盖节点结构定义、插入逻辑、左右旋函数、查找、Size 及高度计算接口设计,以及验证红黑树合法性的方法。相比 AVL 树,红黑树在插入时旋转次数更少,效率更高,广泛应用于 STL map/set 等容器底层。

极客工坊发布于 2026/3/29更新于 2026/6/618 浏览
C++ 红黑树原理与实现:变色旋转及完整代码

C++ 红黑树原理与实现

1 初识红黑树:概念熟悉

红黑树也是一棵二叉搜索树,其每个结点会增加一个存储位(颜色存储位),用来表示结点的颜色(两种颜色),可以是红色或者黑色(因此被称为红黑树)。通过对任何一条从根到叶子的路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出 2 倍,因而是接近平衡的,也就是说,红黑树是近似平衡的。

2 了解红黑树规则

2.1 红黑树的四条规则

2.1.1 红黑树规则

文章配图

2.1.2 结合图示,体会红黑树规则

我们观察几张红黑树的图大家就能有更进一步的体会了——

文章配图 文章配图 文章配图 文章配图

2.1.3 结合图例,理解红黑树的路径数量问题:NIL

文章配图

上面的红黑树里面有几条路径?实际上有 6 条路径。

有人会问,这个 NIL 是啥?这个叫叶子节点,但是这个和我们二叉树里面的叶子结点不是同一个东西!这个叶子结点指的是空节点。有些书籍上也把 NIL 叫做外部结点。NIL 是为了方便准确的标识出所有路径。

说明:《算法导论》等书籍上补充了一条每个叶子结点(NIL)都是黑色的规则。它这里所指的叶子结点不是传统的意义上的叶子结点,而是我们说的空结点。

2.2 红黑树是怎么确保最长路径不超过最短路径的 2 倍的?

  • 由规则 4 可知,从根到 NULL 结点的每条路径都有相同数量的黑色结点,所以极端场景下,最短路径就是全是黑色结点的路径,假设最短路径长度为 bh(blackheight)。
  • 由规则 2 和规则 3 可知,任意一条路径不会有连续的红色结点,所以极端场景下,最长的路径就是一黑一红间隔组成,那么最长路径的长度为 2*bh。
  • 综合红黑树的 4 点规则而言,理论上的全黑最短路径和一黑一红的最长路径并不是在每棵红黑树都所存在的。假设任意一条从根到 NULL 结点路径的长度为 x,那么 bh <= h <= 2*bh。
  • 2.3 理解红黑树的效率

    假设 N 是红黑树树中结点数量,h 最短路径的长度,那么 2^h - 1 <= N < 2^(2 * h) - 1,由此推出 h ≈ logN,也就意味着红黑树增删查改最坏情况也就是走最长路径 2*logN,那么时间复杂度还是 O(logN)。

    红黑树的表达相对 AVL 树要抽象一些,AVL 树的直观在于通过高度差控制了平衡,而红黑树是通过四条规则的颜色约束来间接实现近似平衡,他们效率都是同一档次,但是相对而言,插入相同数量的结点,红黑树的旋转次数是更少的,因为红黑树对平衡的控制没那么严格,所以我们比较常用的还是红黑树,因为红黑树的旋转次数更少,效率还不比 AVL 树低,因此实践中红黑树用得比 AVL 树多,这也是红黑树相比 AVL 树的优势。

    3 实现红黑树

    3.1 红黑树的结构实现

    我们用枚举值来表示一下颜色——

    文章配图

    我们实现一下红黑树的结构——

    文章配图

    3.2 红黑树的插入问题以及插入的代码实现

    3.2.1 红黑树中插入一个值的大致过程

    文章配图

    1、插入一个值按二叉搜索树规则进行插入,插入后我们只需要观察是否符合红黑树的 4 条规则。 2、如果是空树插入,新增结点是黑色结点。如果是非空树插入,新增结点必须红色结点,因为非空树插入,新增黑色结点就破坏了规则 4,规则 4 是很难维护的。 3、非空树插入后,新增结点必须红色结点,如果父亲结点是黑色的,则没有违反任何规则,插入结束。 4、非空树插入后,新增结点必须红色结点,如果父亲结点是红色的,则违反规则 3。进一步分析,c 是红色,p 为红,g 必为黑,这三个颜色都固定了,关键的变化看 u 的情况,需要根据 u 分为以下几种情况分别处理。

    **说明:**下图中假设我们把新增结点标识为 c(cur),c 的父亲标识为 p(parent),p 的父亲标识为 g(grandfather),p 的兄弟标识为 u(uncle)。

    3.2.2 三种情况

    文章配图

    3.2.3 情况 1:变色

    c 为红,p 为红,g 为黑,u 存在且为红,则将 p 和 u 变黑,g 变红。在把 g 当做新的 c,继续往上更新。

    文章配图

    分析:因为 p 和 u 都是红色,g 是黑色,把 p 和 u 变黑,左边子树路径各增加一个黑色结点,g 再变红,相当于保持 g 所在子树的黑色结点的数量不变,同时解决了 c 和 p 连续红色结点的问题,需要继续往上更新是因为,g 是红色,如果 g 的父亲还是红色,那么就还需要继续处理;如果 g 的父亲是黑色,则处理结束了;如果 g 就是整棵树的根,再把 g 变回黑色。

    情况 1 只变色,不旋转。所以无论 c 是 p 的左还是右,p 是 g 的左还是右,都是上面的变色处理方式。

    跟 AVL 树类似,上图向我们展示了一种具体情况,但是实际中需要这样处理的有很多种情况。

    下图将以上类似的处理进行了抽象表达,d / e / f 代表每条路径拥有 hb 个黑色结点的子树,a / b 代表每条路径拥有 hb - 1 个黑色结点的根为红的子树,hb >= 0。

    文章配图

    下图则分别展示了 hb == 0 / hb == 1 / hb == 2 的具体情况组合分析——

    文章配图

    当 hb 等于 2 时,这里组合情况上百亿种,这些样例是帮助我们理解,不论情况多少种,有多么复杂,处理方式都是一样的,变色再继续往上处理即可,所以我们只需要看抽象图即可。

    3.2.4 情况 2:单旋 + 变色

    c 为红,p 为红,g 为黑,u 不存在或者 u 存在且为黑,u 不存在,则 c 一定是新增结点,u 存在且为黑,则 c 一定不是新增,c 之前是黑色的,是在 c 的子树中插入,符合情况 1,变色将 c 从黑色变成红色,更新上来的。

    分析:p 必须变黑,才能解决,连续红色结点的问题,u 不存在或者是黑色的,这里单纯的变色无法解决问题,需要旋转 + 变色。

    文章配图

    如果 p 是 g 的左,c 是 p 的左,那么以 g 为旋转点进行右单旋,再把 p 变黑,g 变红即可。p 变成课这颗树新的根,这样子树黑色结点的数量不变,没有连续的红色结点了,且不需要往上更新,因为 p 的父亲是黑色还是红色或者空都不违反规则。

    文章配图

    如果 p 是 g 的右,c 是 p 的右,那么以 g 为旋转点进行左单旋,再把 p 变黑,g 变红即可。p 变成课这颗树新的根,这样子树黑色结点的数量不变,没有连续的红色结点了,且不需要往上更新,因为 p 的父亲是黑色还是红色或者空都不违反规则。

    文章配图

    3.2.5 情况 3:双旋 + 变色

    c 为红,p 为红,g 为黑,u 不存在或者 u 存在且为黑,u 不存在,则 c 一定是新增结点,u 存在且为黑,则 c 一定不是新增,c 之前是黑色的,是在 c 的子树中插入,符合情况 1,变色将 c 从黑色变成红色,更新上来的。

    分析:p 必须变黑,才能解决,连续红色结点的问题,u 不存在或者是黑色的,这里单纯的变色无法解决问题,需要旋转 + 变色。

    文章配图

    如果 p 是 g 的左,c 是 p 的右,那么先以 p 为旋转点进行左单旋,再以 g 为旋转点进行右单旋,再把 c 变黑,g 变红即可。c 变成课这颗树新的根,这样子树黑色结点的数量不变,没有连续的红色结点了,且不需要往上更新,因为 c 的父亲是黑色还是红色或者空都不违反规则。

    文章配图

    如果 p 是 g 的右,c 是 p 的左,那么先以 p 为旋转点进行右单旋,再以 g 为旋转点进行左单旋,再把 c 变黑,g 变红即可。c 变成课这颗树新的根,这样子树黑色结点的数量不变,没有连续的红色结点了,且不需要往上更新,因为 c 的父亲是黑色还是红色或者空都不违反规则。

    文章配图

    3.2.6 红黑树插入的代码实现

    代码如下所示——

    文章配图 文章配图 文章配图

    3.3 红黑树的旋转

    3.3.1 右旋

    文章配图

    3.3.2 左旋

    文章配图

    3.3.3 左右双旋

    文章配图

    3.3.4 右左双旋

    文章配图

    3.4 红黑树的查找问题

    红黑树的查找就按搜索二叉树的逻辑来实现就可以了,时间复杂度是 O(logn)。

    文章配图

    3.5 Size()

    还是同样的问题,这里传根不能直接传,得从外部调用内部的函数完成传根——

    核心原因:封装性 vs 递归需求的矛盾

    文章配图

    3.5.1 公共接口:不需要传 root
    public: int Size() { return _Size(_root); } // 用户只需调用 tree.Size() 
    int Height() { return _Height(_root); } // 简单、安全、易用
    
    3.5.2 私有实现:必须传 root
    private: int _Size(Node* root) { // 递归必须传当前节点
        if (root == nullptr) return 0;
        return _Size(root->_left) + _Size(root->_right) + 1; // 递归调用
    }
    
    3.5.3 解决方案:双重接口设计

    因此我们的解决方案如下——

    3.5.3.1 公共层(面向用户)
    // 用户看到的简单接口
    tree.Size(); // 不需要参数
    tree.Height(); // 直接使用
    tree.IsBalanceTree();
    
    3.5.3.2 私有层(内部递归)
    // 递归实现的辅助函数
    _Size(root); // 需要节点参数
    _Height(root); // 支持递归遍历
    _IsBalanceTree(root);
    
    3.5.4 为什么要设计成双重接口?
    // 方案 1:让用户传 root(不好)
    tree.Size(someNode); // 用户可能传错节点,不安全
    // 方案 2:放弃递归(不好)
    // 用迭代实现 Size(),但代码复杂,性能可能更差
    // 方案 3:暴露_root(很不好)
    tree._Size(tree._root); // 破坏封装,会很危险,不安全
    
    3.5.5 总结

    文章配图

    3.5.6 Size() 双重接口设计实现

    文章配图

    3.6 Height()

    外部(public)——

    文章配图

    内部(private)——

    文章配图

    3.7 IsBalanceTree()

    外部(public)——

    文章配图

    内部(private)——

    文章配图

    3.8 中序遍历:InOrder()

    外部(public)——

    文章配图

    内部(private)——

    文章配图

    3.9 验证红黑树

    这里获取最长路径和最短路径,检查最长路径不超过最短路径的 2 倍是不可行的,因为就算满足这个条件,红黑树也可能颜色不满足规则,当前暂时没出问题,后续继续插入还是会出问题的。所以我们还是去检查 4 点规则,满足这 4 点规则,一定能保证最长路径不超过最短路径的 2 倍。

    1、规则 1 枚举颜色类型,天然实现保证了颜色不是黑色就是红色。 2、规则 2 直接检查根即可。 3、规则 3 前序遍历检查,遇到红色结点查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲的颜色就方便多了,即孩子不一定存在,父亲一定存在。 4、规则 4 前序遍历,遍历过程中用形参记录根节点到当前结点的 blackNum(黑色结点数量),前序遍历遇到黑色结点就++blackNum,走到空就计算出了一条路径的黑色结点数量。再任意一条路径黑色结点数量作为参考值,依次比较即可。

    文章配图 文章配图 文章配图

    3.10 红黑树的删除

    和 AVL 树那里一样,红黑树这里的删除我们也不做讲解——

    文章配图

    有兴趣的可以去看一下参考资料,书的话推荐看一下《算法导论》或者《STL 源码剖析》,这两本书里面会有讲解。

    完整代码示例与实践演示

    RBTree.h:

    #pragma once
    #include<iostream>
    using namespace std;
    #include<assert.h>
    
    // 枚举值表示颜色
    // 保障不是黑色就是红色
    enum Color { BLACK, RED };
    
    // 这里我们默认按 key / value 结构实现
    // 构造
    template<class K,class V>
    struct RBTreeNode {
        // 更新控制平衡也要加入 parent 指针
        pair<K, V> _kv;
        RBTreeNode<K, V>* _left;
        RBTreeNode<K, V>* _right;
        RBTreeNode<K, V>* _parent;
        Color _col;
        
        // 初始化
        RBTreeNode(const pair<K, V>& kv) :_kv(kv)
            , _left(nullptr)
            , _right(nullptr)
            , _parent(nullptr)
            , _col(RED) {}
    };
    
    template<class K,class V>
    struct RBTree {
        typedef RBTreeNode<K, V> Node;
        // 重命名节点
        public:
        bool Insert(const pair<K, V>& kv) // 红黑树的插入
        {
            if (_root == nullptr) {
                _root = new Node(kv);
                _root->_col = BLACK;
                return true;
            }
            Node* parent = nullptr;
            Node* cur = _root;
            while (cur) {
                if (cur->_kv.first < kv.first) {
                    parent = cur;
                    cur = cur->_right;
                } else if (cur->_kv.first > kv.first) {
                    parent = cur;
                    cur = cur->_left;
                } else {
                    return false;
                }
            }
            // 插入的是红色的节点(新增)
            cur = new Node(kv);
            cur->_col = RED;
            if (parent->_kv.first < kv.first) {
                parent->_right = cur;
            } else {
                parent->_left = cur;
            }
            cur->_parent = parent;
            while (parent && parent->_col == RED) {
                Node* grandfather = parent->_parent;
                if (grandfather->_left == parent) {
                    // g
                    // p u
                    // c
                    Node* uncle = grandfather->_right; // 叔叔存在且为黑
                    if (uncle && uncle->_col == RED) {
                        // 变色 + 继续往上处理
                        parent->_col = uncle->_col = BLACK;
                        grandfather->_col = RED;
                        cur = grandfather;
                        parent = cur->_parent;
                    } else {
                        // 叔叔不存在或者叔叔存在且为黑
                        // g
                        // p u
                        // c
                        // 单旋 + 变色
                        if (cur == parent->_left) {
                            RotateR(grandfather);
                            parent->_col = BLACK;
                            grandfather->_col = RED;
                        } else {
                            // g
                            // p u
                            // c
                            // 双旋 + 变色
                            RotateL(parent);
                            RotateR(grandfather);
                            cur->_col = BLACK;
                            grandfather->_col = RED;
                        }
                        break;
                    }
                } else {
                    // g
                    // u p
                    Node* uncle = grandfather->_left; // 叔叔存在且为红->变色即可
                    if (uncle && uncle->_col == RED) {
                        parent->_col = uncle->_col = BLACK;
                        grandfather->_col = RED;
                        // 继续往上处理
                        cur = grandfather;
                        parent = cur->_parent;
                    } else {
                        // 情况二:叔叔不存在或者存在且为黑
                        // 旋转 + 变色
                        // g
                        // u p
                        // c
                        if (cur == parent->_right) {
                            RotateL(grandfather);
                            parent->_col = BLACK;
                            grandfather->_col = RED;
                        } else {
                            // g
                            // u p
                            // c
                            RotateR(parent);
                            RotateL(grandfather);
                            grandfather->_col = RED;
                        }
                        break;
                    }
                }
            }
            _root->_col = BLACK; // 不去判断,直接变成黑的
            return true;
        }
        
        // -------------旋转----------------
        // 右旋
        void RotateR(Node* parent) {
            Node* subL = parent->_left;
            Node* subLR = subL->_right;
            parent->_left = subLR;
            if (subLR) subLR->_parent = parent;
            Node* parentParent = parent->_parent;
            subL->_right = parent;
            parent->_parent = subL;
            if (parent == _root) {
                _root = subL;
                subL->_parent = nullptr;
            } else {
                if (parentParent->_left == parent) {
                    parentParent->_left = subL;
                } else {
                    parentParent->_right = subL;
                }
                subL->_parent = parentParent;
            }
        }
        
        // 左旋
        void RotateL(Node* parent) {
            Node* subR = parent->_right;
            Node* subRL = subR->_left;
            // 处理 subRL 与 parent 的关系
            parent->_right = subRL;
            if (subRL) subRL->_parent = parent;
            Node* parentParent = parent->_parent;
            // 处理 subR 与 parent 的关系
            subR->_left = parent;
            parent->_parent = subR;
            // 处理 subR 与 parentParent 的关系
            if (parentParent == nullptr) {
                _root = subR;
                subR->_parent = nullptr;
            } else {
                if (parent == parentParent->_left) {
                    parentParent->_left = subR;
                } else {
                    parentParent->_right = subR;
                }
                subR->_parent = parentParent;
            }
        }
        
        // 中序遍历
        void InOrder() {
            _InOrder(_root);
            cout << endl;
        }
        
        // 查找
        Node* Find(const K& key) {
            Node* cur = _root;
            while (cur) {
                if (cur->_kv.first < key) {
                    cur = cur->_right;
                } else if(cur->_kv.first > key) {
                    cur = cur->_left;
                } else {
                    return cur;
                }
            }
            return nullptr;
        }
        
        bool IsBalanceTree() {
            if (_root && _root->_col == RED) return false; // 最左路径黑色节点的数量做参考值,去比较其他路径
            int left_bn = 0;
            Node* cur = _root;
            while (cur) {
                if (cur->_col == BLACK) left_bn++;
                cur = cur->_left; // 不加上会死循环
            }
            return _Checkcolor(_root, 0, left_bn);
        }
        
        int Height() {
            return _Height(_root);
        }
        
        int Size() {
            return _Size(_root);
        }
        
        private:
        // 传根,不能直接传,外部调用内部
        int _Size(Node* root) {
            if (root == nullptr) return 0;
            return _Size(root->_left) + _Size(root->_right) + 1;
        }
        
        int _Height(Node* root) {
            if (root == nullptr) return 0;
            int leftHeight = _Height(root->_left);
            int rightHeight = _Height(root->_right);
            return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
        }
        
        // root_cur_bn 根节点到当前节点路径上的黑色节点的数量
        // 前序递归
        bool _Checkcolor(Node* root, int root_cur_bn,const int left_bn) {
            if (root == nullptr) {
                // 检查每条路径的黑色节点数量
                if (root_cur_bn != left_bn) {
                    cout << "黑色节点的数量不相等" << endl;
                    return false;
                }
                return true;
            }
            if (root->_col == BLACK) {
                root_cur_bn++;
            }
            // 检查连续的红色节点
            if (root->_col == RED && root->_parent && root->_parent->_col == RED) {
                cout << root->_kv.first << "存在连续的红色节点" << endl;
                return false;
            }
            return _Checkcolor(root->_left, root_cur_bn, left_bn) && _Checkcolor(root->_right, root_cur_bn, left_bn);
        }
        
        int _InOrder(Node* root) {
            if (root == nullptr) return 0;
            _InOrder(root->_left);
            //cout << root->_kv.first << ":" << root->_kv.second << endl;
            cout << root->_kv.first << " ";
            _InOrder(root->_right);
            ////cout << root->_kv.first << ":" << root->_kv.second << endl;
        }
        
        private:
        Node* _root = nullptr;
    };
    

    Tree.cpp:

    #define _CRT_SECURE_NO_WARNINGS 1
    #include<iostream>
    #include<vector>
    using namespace std;
    #include"RBTree.h"
    
    void TestRBTree1() {
        RBTree<int, int>t; // 常规的测试用例
        int a[] = { 4,2,6,1,3,5,15,7,16,14 };
        for (auto e : a) {
            if (e == 7) {
                int i = 0;
            }
            t.Insert({ e,e });
            cout << "Insert:" << e << "->";
            t.InOrder();
        }
        t.InOrder(); //cout << t.IsBalanceTree() << endl;
    }
    
    // 插入一堆随机值,测试平衡,顺便测试一下高度和性能
    void TestRBTree2() {
        const int N = 1000000;
        vector<int> v;
        v.reserve(N);
        srand(time(0));
        for (size_t i = 0; i < N; i++) {
            v.push_back(rand() + i);
        }
        size_t begin2 = clock();
        RBTree<int, int> t;
        for (auto e : v) {
            t.Insert(make_pair(e, e));
        }
        size_t end2 = clock();
        cout << "Insert:" << end2 - begin2 << endl;
        cout << t.IsBalanceTree() << endl;
        cout << "Height:" << t.Height() << endl;
        cout << "Size:" << t.Size() << endl;
        size_t begin1 = clock(); // 确定在的值
        /*for (auto e : v) { t.Find(e); }*/
        // 随机值
        for (size_t i = 0; i < N; i++) {
            t.Find((rand() + i));
        }
        size_t end1 = clock();
        cout << "Find:" << end1 - begin1 << endl;
    }
    
    int main() {
        //TestRBTree1();
        TestRBTree2();
        return 0;
    }
    

    运行结果

    TestRBTree1()

    文章配图

    TestRBTree2()

    文章配图

    目录

    1. C++ 红黑树原理与实现
    2. 1 初识红黑树:概念熟悉
    3. 2 了解红黑树规则
    4. 2.1 红黑树的四条规则
    5. 2.1.1 红黑树规则
    6. 2.1.2 结合图示,体会红黑树规则
    7. 2.1.3 结合图例,理解红黑树的路径数量问题:NIL
    8. 2.2 红黑树是怎么确保最长路径不超过最短路径的 2 倍的?
    9. 2.3 理解红黑树的效率
    10. 3 实现红黑树
    11. 3.1 红黑树的结构实现
    12. 3.2 红黑树的插入问题以及插入的代码实现
    13. 3.2.1 红黑树中插入一个值的大致过程
    14. 3.2.2 三种情况
    15. 3.2.3 情况 1:变色
    16. 3.2.4 情况 2:单旋 + 变色
    17. 3.2.5 情况 3:双旋 + 变色
    18. 3.2.6 红黑树插入的代码实现
    19. 3.3 红黑树的旋转
    20. 3.3.1 右旋
    21. 3.3.2 左旋
    22. 3.3.3 左右双旋
    23. 3.3.4 右左双旋
    24. 3.4 红黑树的查找问题
    25. 3.5 Size()
    26. 3.5.1 公共接口:不需要传 root
    27. 3.5.2 私有实现:必须传 root
    28. 3.5.3 解决方案:双重接口设计
    29. 3.5.3.1 公共层(面向用户)
    30. 3.5.3.2 私有层(内部递归)
    31. 3.5.4 为什么要设计成双重接口?
    32. 3.5.5 总结
    33. 3.5.6 Size() 双重接口设计实现
    34. 3.6 Height()
    35. 3.7 IsBalanceTree()
    36. 3.8 中序遍历:InOrder()
    37. 3.9 验证红黑树
    38. 3.10 红黑树的删除
    39. 完整代码示例与实践演示
    40. RBTree.h:
    41. Tree.cpp:
    42. 运行结果
    43. TestRBTree1()
    44. TestRBTree2()
    • 💰 8折买阿里云服务器限时8折了解详情
    • Magick API 一键接入全球大模型注册送1000万token查看
    • 🤖 一键搭建Deepseek满血版了解详情
    • 一键打造专属AI 智能体了解详情
    极客日志微信公众号二维码

    微信扫一扫,关注极客日志

    微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

    更多推荐文章

    查看全部
    • AI 模型调优实战:Python 核心技术与最佳实践
    • WebStorm 2025 版下载安装图文教程
    • 字节开源 DeerFlow 2.0:Super Agent 运行时基础设施详解
    • 基于LLama-Factory的游戏NPC对话逻辑优化实践
    • 知网 AIGC 检测标准与论文降重实战指南
    • 高级人工智能期末复习:搜索算法
    • 基于 SpringBoot 的超市会员积分管理系统设计与实现
    • Git 实现本地与远程仓库文件传递方法
    • 服务器或本地部署鸣潮 QQ 机器人,实现签到与练度查询功能
    • 将 Zed 编辑器集成到 Bright Data Web MCP
    • 6 款高质量 AI 绘画工具推荐与使用指南
    • 基于 Higress 将 REST API 转换为 MCP Server 工具
    • ns-3 虚拟现实突发流量框架实现与建模分析
    • Style2Paints 风格迁移数据集与训练实战解析
    • Spring AOP 核心概念与实战应用详解
    • 二叉树算法实战:美国血统重建与深度宽度计算
    • Elasticsearch + Kibana 实战指南:从安装部署到 C++ 客户端封装
    • Web 应用全栈开发实践:从前端到后端
    • VSCode 中关闭 GitHub Copilot 功能
    • Vue 3 + Hiprint 打印设计器 vg-print:拖拽设计与静默打印方案

    相关免费在线工具

    • 加密/解密文本

      使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

    • Gemini 图片去水印

      基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

    • Markdown转HTML

      将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

    • HTML转Markdown

      将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online