C/C++基础

指针

在这里插入图片描述

链表

请添加图片描述

二叉树

请添加图片描述

二叉搜索树

请添加图片描述

平衡二叉树(AVL)

请添加图片描述

源码实现

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

typedef struct _avltree_t {
    int                val;
    int                height;
    struct _avltree_t* left;
    struct _avltree_t* right;
} avltree_t;

avltree_t* make_node(int val)
{
    avltree_t* node = (avltree_t*)malloc(sizeof(avltree_t));
    if (node == NULL) {
        perror("Failed to allocate memory for new node");
        exit(EXIT_FAILURE);
    }

    node->val    = val;
    node->height = 1;
    node->left   = NULL;
    node->right  = NULL;
    return node;
}

int get_height(avltree_t* root)
{
    if (root == NULL) {
        return 0;
    }

    return root->height;
}

void update_height(avltree_t* root)
{
    root->height =
        1 + (get_height(root->left) > get_height(root->right) ? get_height(root->left) : get_height(root->right));
}

int get_balance(avltree_t* root)
{
    if (root == NULL) {
        return 0;
    }

    return get_height(root->left) - get_height(root->right);
}

/**
 * @brief 1. 当前节点的右子树作为新树的根节点
 *        2. 当前节点作为新树根节点的左子树
 *        如果新的树根有左子树,则新树根的左子树就作为旧根节点的右子树
 *
 * @param root
 * @return avltree_t*
 */
avltree_t* left_rotation(avltree_t* root)
{
    // 1. 当前节点的右子树作为新树的根节点
    avltree_t* new_root = root->right;
    // 新的树根有左子树
    avltree_t* new_root_left = new_root->left;
    // 2. 当前节点作为新树的根节点的左子树
    new_root->left = root;
    // 如果新的树根有左子树,则新树根的左子树就作为旧根节点的右子树
    root->right = new_root_left;
    // 更新树高
    update_height(root);
    update_height(new_root);
    return new_root;
}

/**
 * @brief 1. 当前节点的左子树作为新的根节点
 *        2. 当前节点作为新树根节点的右子树
 *        如果新树根有右子树,则新树根的右子树作为旧根节点的左子树
 *
 * @param root
 * @return avltree_t*
 */
avltree_t* right_rotation(avltree_t* root)
{
    // 1. 当前节点的左子树作为新的根节点
    avltree_t* new_root = root->left;
    // 新树根有右子树
    avltree_t* new_root_right = new_root->right;
    // 2. 当前节点作为新树根节点的右子树
    new_root->right = root;
    // 如果新树根有右子树,则新树根的右子树作为旧根节点的左子树
    root->left = new_root_right;
    // 更新树高
    update_height(root);
    update_height(new_root);
    return new_root;
}

avltree_t* insert_node(avltree_t* root, int val)
{
    if (!root) {
        return make_node(val);
    }

    if (val < root->val) {
        root->left = insert_node(root->left, val);
    }
    if (val > root->val) {
        root->right = insert_node(root->right, val);
    } else {
        return root;
    }

    update_height(root);
    int balance = get_balance(root);
    // 1. LL
    if (balance > 1 && val < root->left->val)
        return right_rotation(root);
    // 2. RR
    if (balance < -1 && val > root->right->val)
        return left_rotation(root);
    // 3. LR
    if (balance > 1 && val > root->left->val) {
        root->left = left_rotation(root->left);
        return right_rotation(root);
    }
    // 4. RL
    if (balance < -1 && val < root->right->val) {
        root->right = right_rotation(root->right);
        return left_rotation(root);
    }

    return root;
}

/**
 * @brief 删除节点
 *
 * @param root
 * @param val
 * @return avltree_t*
 */
avltree_t* delete_node(avltree_t* root, int val)
{
    if (!root) {
        return root;
    }

    if (val < root->val) {
        root->left = delete_node(root->left, val);
    } else if (val > root->val) {
        root->right = delete_node(root->right, val);
    } else {
        if (!root->left || !root->right) {
            avltree_t* temp = root->left ? root->left : root->right;
            free(root);
            return temp;
        }

        avltree_t* minLR = root->right;
        while (minLR->left)
            minLR = minLR->left;
        root->val   = minLR->val;
        root->right = delete_node(root->right, minLR->val);
    }

    // 更新高度
    update_height(root);

    // 检查平衡并旋转
    int balance = get_balance(root);
    if (balance > 1 && get_balance(root->left) >= 0) {
        return right_rotation(root);
    }

    if (balance < -1 && get_balance(root->right) <= 0) {
        return left_rotation(root);
    }

    if (balance > 1 && get_balance(root->left) < 0) {
        root->left = left_rotation(root->left);
        return right_rotation(root);
    }

    if (balance < -1 && get_balance(root->right) > 0) {
        root->right = right_rotation(root->right);
        return left_rotation(root);
    }

    return root;
}

void mid_tree(avltree_t* T)
{
    if (NULL == T) {
        return;
    }

    mid_tree(T->left);
    std::cout << T->val << std::endl;
    // std::cout << T << std::endl;
    // std::cout << T->left << std::endl;
    // std::cout << T->right << std::endl << std::endl;
    mid_tree(T->right);
}

void pre_tree(avltree_t* T)
{
    if (NULL == T) {
        return;
    }

    std::cout << T->val << std::endl;
    pre_tree(T->left);
    // std::cout << T << std::endl;
    // std::cout << T->left << std::endl;
    // std::cout << T->right << std::endl << std::endl;
    pre_tree(T->right);
}

int main(int argc, char** argv)
{
    avltree_t* T = NULL;

    T = insert_node(T, 17);
    T = insert_node(T, 11);
    T = insert_node(T, 7);
    T = insert_node(T, 16);
    T = insert_node(T, 5);
    T = insert_node(T, 19);
    T = insert_node(T, 9);
    T = insert_node(T, 14);
    T = insert_node(T, 12);
    T = insert_node(T, 27);

    std::cout << "----------------------- make avtree" << std::endl;
    mid_tree(T);

    T = delete_node(T, 19);
    T = delete_node(T, 12);

    std::cout << "----------------------- delete node" << std::endl;
    mid_tree(T);
    return 0;
}

红黑树

红黑树
红黑树(Red Black Tree)是一颗自平衡的二叉搜索树(BST),它保持了二叉搜索树的所有性质,树上的每一个节点都遵循以下规则:

  • 每个节点要么是红色,要么是黑色
    • 红黑树中的每一个节点都有一个颜色属性,可以是红色或黑色
  • 根节点是黑色的
    • 红黑树的根节点必须为黑色,以确保树的高度从根到叶子是平衡的
  • 所有叶子节点(NIL节点,空节点)都是黑色的
    • 树的外部节点(没有子节点的节点)都是黑色,这些节点通常被称为叶子节点或NIL节点
  • 如果一个节点是红色的,则它的两个子节点都是黑色的
    • 没有两个连续的红色节点(即,一个红色的节点不能有红色的子节点),这确保了从根到叶子的最长路径不会超过最短路径的两倍
  • 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点
    • 从树的任何节点到它的所有叶子节点的路径上,黑色节点的数量必须相同,这保证了树的平衡性

红黑树的插入

红黑树的插入节点可以为红色也可以为黑色,但是当插入节点为黑色时,必然破坏红黑树的性质,需要进行调整,而插入节点为红色时,可能不存在调整红黑树的情况,因此红黑树的插入节点为红色

红黑树失衡分析

插入节点

插入节点导致不平衡情况分析: 假设插入的节点为u插入节点的父节点为pu插入节点的祖父节点为gu插入节点的叔叔节点为y,破坏红黑树的原因时存在连续2个红色的节点,因此u为红色pu为红色,又因根节点时黑色,因此pu肯定还有一个父节点为黑色,因此gu也必定存在。
不平衡可以分为:

  • LLb: pu为gu的左孩子,u为pu的左孩子,gu的另一个孩子(右孩子)为黑色
  • LLr: pu为gu的左孩子,u为pu的左孩子,gu的另一个孩子(右孩子)为红色
  • LRb: pu为gu的左孩子,u为pu的右孩子,gu的另一个孩子(右孩子)为黑色
  • LRr: pu为gu的左孩子,u为pu的右孩子,gu的另一个孩子(右孩子)为红色
  • RRb: pu为gu的右孩子,u为pu的右孩子,gu的另一个孩子(左孩子)为黑色
  • RRr: pu为gu的右孩子,u为pu的右孩子,gu的另一个孩子(右孩子)为红色
  • RLb: pu为gu的右孩子,u为pu的左孩子,gu的另一个孩子(左孩子)为黑色
  • RLr: pu为gu的右孩子,u为pu的左孩子,gu的另一个孩子(左孩子)为红色
    请添加图片描述

失衡调节方式

  • 左旋
    • 当前节点(u)的右子树作为新的根节点(y),当前节点作为新根节点的左子树
    • 如果新的树根(y)有左子树,则原来的左子树作为旧根(u)的右子树
  • 右旋
    • 当前节点(u)的左子树作为新树的根节点(x),当前节点作为新树根节点的右子树
    • 如果新的树根原来有右子树,则原来的右子树作为旧树根的左子树
      请添加图片描述
      请添加图片描述

红黑树的构建

以构建*{17, 18, 23, 34, 27, 15, 9, 6, 8, 5, 25}*的红黑树为例剖析红黑树失衡调节
请添加图片描述
请添加图片描述
请添加图片描述

源码实现

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <queue>

#define RED   1
#define BLACK 2
typedef int KEY_TYPE;
// 定义红黑树的节点
typedef struct _rbtree_node {
    KEY_TYPE             key;
    void*                value;
    struct _rbtree_node* left;
    struct _rbtree_node* right;
    struct _rbtree_node* parent;
    unsigned char        color;
} rbtree_node_t;

// 定义红黑树的根节点,指向头节点和叶子节点
typedef struct _rbtree {
    rbtree_node_t* root;  // 头节点
    rbtree_node_t* nil;  // 叶子节点
} rbtree_t;

void           rbtree_insert_fixup(rbtree_t* T, rbtree_node_t* u);
rbtree_node_t* rbtree_twice(rbtree_t* T, rbtree_node_t* u);
rbtree_node_t* rbtee_right_mini(rbtree_t* T, rbtree_node_t* u);
rbtree_node_t* rbtee_left_max(rbtree_t* T, rbtree_node_t* u);
void           rbtree_delete_fixup(rbtree_t* T, rbtree_node_t* u);

/**
 * @brief 左旋转
 *  当前节点(u)的右子树作为新的根节点(y),当前节点作为新根节点的左子树,
 *  如果新的树根(y)有左子树,则原来的左子树作为旧根(u)的右子树
 * @param T 红黑树
 * @param u 当前旋转的根节点
 */
void rbtree_left_rotate(rbtree_t* T, rbtree_node_t* u)
{
    rbtree_node_t* y = u->right;
    // 1.u的右节点作为新根节点的左节点
    u->right = y->left;
    if (y->left != T->nil) {
        // y的左节点不为叶子节点,则左节点作为u的右子树
        y->left->parent = u;
    }

    // 2. 调整父节点
    y->parent = u->parent;
    if (u->parent == T->nil) {
        T->root = y;
    } else if (u == u->parent->left) {
        // u 为父节点的左子树,将u的父节点的左子树设置为y
        u->parent->left = y;
    } else {
        // u 为父节点的左子树,则将u的父节点的左子树设置为y
        u->parent->right = y;
    }

    // 3. 调整新根节点的左节点
    y->left   = u;
    u->parent = y;
}

/**
 * @brief 右旋
 *  当前节点(u)的左子树作为新树的根节点(x),当前节点作为新树根节点的右子树
 *  如果新的树根原来有右子树,则原来的右子树作为旧树根的左子树
 * @param T 红黑树
 * @param u 当前旋转的根节点
 */
void rbtree_right_rotate(rbtree_t* T, rbtree_node_t* u)
{
    rbtree_node_t* x = u->left;
    u->left          = x->right;
    if (x->right != T->nil) {
        x->right->parent = u;
    }

    x->parent = u->parent;
    if (u->parent == T->nil) {
        T->root = x;
    } else if (u == u->parent->left) {
        u->parent->left = x;
    } else {
        u->parent->right = x;
    }

    x->right  = u;
    u->parent = x;
}

/**
 * @brief 红黑树在插入任何节点之前,它本身就已经是一颗红黑树归纳法:
 *  插入的节点位于最底层,且初始颜色为红色,然后根据颜色做做相关的调整
 *
 * @param T 红黑树
 * @param u 插入的节点
 */
void rbtree_insert(rbtree_t* T, rbtree_node_t* u)
{
    rbtree_node_t* y = T->root;  // 新插入节点u的插入点
    rbtree_node_t* x = T->root;  // 指向头节点
    while (x != T->nil) {
        y = x;
        if (u->key < x->key) {
            x = x->left;
        } else if (u->key > x->key) {
            x = x->right;
        } else {
            // 红黑树中已经存在,直接返回
            return;
        }
    }
    /** 此时
     *     y -----> xx  or   xx
     *              /         \
     *     x ----> nil        nil
     */
    u->parent = y;
    if (y == T->nil) {
        // 原来的树为空则新插入的节点作为根节点
        T->root = u;
    } else if (u->key < y->key) {
        // 插入的是左节点
        y->left = u;
    } else {
        y->right = u;
    }

    u->left  = T->nil;
    u->right = T->nil;
    u->color = RED;  // 插入的新节点一开始统一为红色节点
    // 插入颜色调整
    rbtree_insert_fixup(T, u);
}

/**
 * @brief 插入节点后颜色的调整
 *
 * @param T
 * @param u
 */
void rbtree_insert_fixup(rbtree_t* T, rbtree_node_t* u)
{
    while (u->parent->color == RED) {
        if (u->parent == u->parent->parent->left) {
            /**     gu            gu
             *     /  \          /  \
             *   pu    y       pu   y
             *   /              \
             *  u                u
             */
            rbtree_node_t* y = u->parent->parent->right;
            if (y->color == RED) {
                // LLr  LRr: 变色
                // pu变黑色,y变黑色,gu变红色
                u->parent->color         = BLACK;
                y->color                 = BLACK;
                u->parent->parent->color = RED;
                // gu变为u继续对u进行判断
                u = u->parent->parent;
            } else {
                // LLb LRb:变色+旋转
                if (u == u->parent->right) {
                    /*  LRb: pu当成u,对u进行左旋
                     *     gu
                     *    /  \
                     *   pu   y
                     *    \
                     *     u
                     */
                    u = u->parent;
                    rbtree_left_rotate(T, u);
                }
                // pu变黑色,gu变红色
                u->parent->color         = BLACK;
                u->parent->parent->color = RED;
                // 对gu进行右旋转
                rbtree_right_rotate(T, u->parent->parent);
            }
        } else {
            /**     gu            gu
             *     /  \          /  \
             *   y    pu        y   pu
             *        /              \
             *       u                u
             */
            rbtree_node_t* y = u->parent->parent->left;
            if (y->color == RED) {
                // RLr RRr: 变色
                // pu变黑色,y变黑色,gu变红色
                u->parent->color         = BLACK;
                y->color                 = BLACK;
                u->parent->parent->color = RED;
                // gu变为u继续对u进行判断
                u = u->parent->parent;
            } else {
                // RLb RRb: 变色+旋转
                if (u == u->parent->left) {
                    /*  RLb: pu当成u,对u进行右旋
                     *      gu
                     *     /  \
                     *   y    pu
                     *        /
                     *       u
                     */
                    u = u->parent;
                    rbtree_right_rotate(T, u);
                }
                // pu变黑色,gu变红色
                u->parent->color         = BLACK;
                u->parent->parent->color = RED;
                // gu进行左旋
                rbtree_left_rotate(T, u->parent->parent);
            }
        }
    }
    // 根节点始终为黑色
    T->root->color = BLACK;
}

/**
 * @brief 红黑树的删除
 *
 * @param T 根节点
 * @param u 要删除的子节点
 */
void rbtree_delete(rbtree_t* T, rbtree_node_t* u)
{
    rbtree_node_t* y = T->nil;  // y指向要删除/移动替换的结点
    rbtree_node_t* x = T->nil;

    if (u->left == T->nil || u->right == T->nil) {
        y = u;
    } else {
        // u 左右节点都存在
        y = rbtree_twice(T, u);
    }

    if (y->left != T->nil) {
        x = y->left;
    } else if (y->right != T->nil) {
        x = y->right;
    }

    x->parent = y->parent;
    if (y->parent == T->nil) {
        T->root = x;
    } else if (y == y->parent->left) {
        y->parent->left = x;
    } else {
        y->parent->right = x;
    }

    if (y != u) {
        u->key   = y->key;
        u->value = y->value;
    }

    if (y->color == BLACK) {
        rbtree_delete_fixup(T, x);
    }
}

/**
 * @brief 找到后继节点,即右子树中最小的节点
 *
 * @param T
 * @param u
 * @return rbtree_node_t*
 */
rbtree_node_t* rbtee_right_mini(rbtree_t* T, rbtree_node_t* u)
{
    while (u->left != T->nil) {
        u = u->left;
    }

    return u;
}

/**
 * @brief 找到前驱节点:即左子树中最大的节点
 *
 * @param T
 * @param u
 * @return rbtree_node_t*
 */
rbtree_node_t* rbtee_left_max(rbtree_t* T, rbtree_node_t* u)
{
    while (u->right != T->nil) {
        u = u->right;
    }

    return u;
}

/**
 * @brief 删除的节点有左右子树
 *
 * @param T
 * @param u
 * @return rbtree_node_t*
 */
rbtree_node_t* rbtree_twice(rbtree_t* T, rbtree_node_t* u)
{
    rbtree_node_t* k = u->parent;
    // 找到后继节点:即中序遍历时右子树的第一个节点
    if (u->right != T->nil) {
        return rbtee_right_mini(T, u->right);
    }

    while ((k != T->nil) && u == k->right) {
        u = k;
        k = k->parent;
    }

    return k;
}

/**
 * @brief 红黑树删除调整
 *
 * @param T
 * @param u
 */
void rbtree_delete_fixup(rbtree_t* T, rbtree_node_t* u)
{
    while ((u != T->root) && (u->color == BLACK))  // 不是根节点且删除的节点为黑
    {
        // 如果u位于父节点的左边
        if (u == u->parent->left) {
            // 找到兄弟节点
            rbtree_node_t* w = u->parent->right;
            // 情况1,兄弟节点为红色
            if (w->color == RED) {
                // 1.1兄弟节点变成黑色
                w->color = BLACK;
                // 1.2 父节点变成红色
                u->parent->color = RED;
                // 1.3 父节点左旋
                rbtree_left_rotate(T, u->parent);
                // 重新设置u的兄弟节点
                w = u->parent->right;
            }
            // 情况2
            if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
                // 兄弟节点变为红色
                w->color = RED;
                u        = u->parent;
            } else {
                // 情况3 u的兄弟节点是黑色的,兄弟的左孩子是红色,右孩子是黑色
                if ((w->right->color == BLACK)) {
                    // 3.1兄弟节点变红
                    w->color = RED;
                    // 将左孩子变黑
                    w->left->color = BLACK;
                    // 已兄弟节点右旋
                    rbtree_right_rotate(T, w);
                    // 重置u的兄弟节点
                    w = u->parent->right;
                }
                // 情况4 u的兄弟节点是黑色,u的兄弟节点的右孩子是红色的
                // 将兄弟节点换成父节点的颜色
                w->color = u->parent->color;
                // 把付姐带你和兄弟节点的右孩子涂黑
                w->parent->color = BLACK;
                w->right->color  = BLACK;
                // 对父节点左旋
                rbtree_left_rotate(T, u->parent);
                // 设置u指针,指向根节点
                u = T->root;  // 结束代码
            }
        } else  // 如果u位于u父节点的右边
        {
            rbtree_node_t* w = u->parent->left;
            if (w->color == RED) {
                w->color         = BLACK;
                u->parent->color = RED;
                rbtree_right_rotate(T, u->parent);
                w = u->parent->left;
            }

            if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
                w->color = RED;
                u        = u->parent;
            } else {
                if (w->left->color == BLACK) {
                    w->right->color = BLACK;
                    w->color        = RED;
                    rbtree_left_rotate(T, w);
                    w = u->parent->left;
                }

                w->color         = u->parent->color;
                u->parent->color = BLACK;
                w->left->color   = BLACK;
                rbtree_right_rotate(T, u->parent);

                u = T->root;
            }
        }
    }
    // 继承节点变为黑色,为了弥补失去的黑高
    u->color = BLACK;
}

/**
 * @brief 红黑树查找
 *
 * @param T
 * @param key
 * @return rbtree_node_t* 查到的节点
 */
rbtree_node_t* rbtree_search(rbtree_t* T, KEY_TYPE key)
{
    rbtree_node_t* node = T->root;
    while (node != T->nil) {
        if (key < node->key) {
            node = node->left;
        } else if (key > node->key) {
            node = node->right;
        } else {
            return node;
        }
    }

    return T->nil;
}

/**
 * @brief 红黑树中序遍历
 *
 * @param T
 * @param node
 */
void rbtree_traversal_center(rbtree_t* T, rbtree_node_t* node)
{
    if (node != T->nil) {
        rbtree_traversal_center(T, node->left);
        if (node == node->parent->left) {
            printf("Left  key: %2d color: %s\n", node->key, (node->color == RED) ? "RED" : "BLACK");
        } else if (node == node->parent->right) {
            printf("Right key: %2d color: %s\n", node->key, (node->color == RED) ? "RED" : "BLACK");
        } else {
            printf("Root  key: %2d color: %s\n", node->key, (node->color == RED) ? "RED" : "BLACK");
        }
        rbtree_traversal_center(T, node->right);
    }
}

rbtree_node_t* rbtree_make_node(rbtree_t* T, int key)
{
    rbtree_node_t* node = (rbtree_node_t*)malloc(sizeof(rbtree_node_t));
    if (node == NULL) {
        return NULL;
    }

    node->color  = RED;
    node->key    = key;
    node->value  = NULL;
    node->left   = T->nil;
    node->right  = T->nil;
    node->parent = T->nil;
    return node;
}

void rbtree_traversal_bfs(rbtree_t* T, rbtree_node_t* node)
{
    std::queue<rbtree_node_t*> dq;
    dq.emplace(node);
    while (!dq.empty()) {
        rbtree_node_t* node = dq.front();
        dq.pop();
        if (node == node->parent->left) {
            printf("Left  key: %2d color: %s\n", node->key, (node->color == RED) ? "RED" : "BLACK");
        } else if (node == node->parent->right) {
            printf("Right key: %2d color: %s\n", node->key, (node->color == RED) ? "RED" : "BLACK");
        } else {
            printf("Root  key: %2d color: %s\n", node->key, (node->color == RED) ? "RED" : "BLACK");
        }

        if (node->left != T->nil) {
            dq.emplace(node->left);
        }

        if (node->right != T->nil) {
            dq.emplace(node->right);
        }
    }
}

void rbtree_destroy_node(rbtree_t* T, rbtree_node_t* node)
{
    if (node == T->nil) {
        return;
    }

    rbtree_destroy_node(T, node->left);
    rbtree_destroy_node(T, node->right);
    printf("key: %2d free %p\n", node->key, node);
    free(node);
    node = NULL;
}

void rbtree_destroy(rbtree_t* T)
{
    rbtree_destroy_node(T, T->root);
    free(T->nil);
    free(T);
}

int main(int argc, char* argv[])
{
    int       keys[] = {17, 18, 23, 34, 27, 15, 9, 6, 8, 5, 25};
    rbtree_t* T      = (rbtree_t*)malloc(sizeof(rbtree_t));
    if (T == NULL) {
        printf("malloc failure\n");
        return -1;
    }

    T->nil        = (rbtree_node_t*)malloc(sizeof(rbtree_node_t));
    T->nil->color = BLACK;
    T->root       = T->nil;

    for (int i = 0; i < 11; ++i) {
        rbtree_node_t* u = rbtree_make_node(T, keys[i]);
        if (u) {
            rbtree_insert(T, u);
        }
    }

    printf("ROOT key: %d color: %s\n", T->root->key, (T->root->color == RED) ? "RED" : "BLACK");

    printf("----------------- rbtree_traversal_center\n");
    rbtree_traversal_center(T, T->root);

    printf("----------------- rbtree_traversal_bfs\n");

    rbtree_traversal_bfs(T, T->root);
    printf("----------------- rbtree_destroy\n");
    rbtree_destroy(T);

    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

血_影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值