C/C++平衡二叉树实现 —— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(设计详解)

输出展示:
在这里插入图片描述

相关文章目录

MFC二叉树可视化绘制 (C++)—— 插入、删除、先序遍历、中序遍历、后序遍历、层序遍历(基于平衡二叉树实现)

前言

该程序由c语言编写,可运行于vs环境下,可轻易转换成C++代码。

一、实现功能

实现操作:结点的插入、删除。
实现输出:输出先序遍历、中序遍历、后序遍历、层序遍历的结果。

二、代码设计

1. 定义平衡二叉树的结点结构

平衡二叉树的结点结构:

// 平衡二叉树的结点结构
typedef struct BiTNode
{
    int data;
    int bf;             // 平衡因子
    BiTNode* lchild, * rchild;
}BiTNode, * BiTree;

BiTree 为指向树结点的指针,用于表示树对象。

2. 定义层序遍历链队列相关结构和函数

链队列结构:

/* 链队列结点结构(用于层序遍历) */
typedef struct QNode {
    BiTree t;                   // 结点数据域
    struct QNode* next;        // 结点指针域
}QNode, * QueuePtr;

/* 链队列结构(用于层序遍历) */
typedef struct {
    QueuePtr front;     // 队首指针
    QueuePtr rear;      // 队尾指针
}LinkQueue;

初始化链队列函数、进队函数、出队函数定义:

/* 初始化链队列函数 */
Status InitQueue(LinkQueue& Q)
{
    // 创建一个带附加头结点的空链队列
    Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
    if (!Q.front) {
        return ERROR;
    }
    Q.front->next = NULL;
    return OK;
}

/* 进队函数 */
/* e:插入元素 */
Status EnQueue(LinkQueue& Q, BiTree e)
{
    // 将元素e插入到链队列中
    QueuePtr p;
    p = (QueuePtr)malloc(sizeof(QNode));
    if (!p) {
        return ERROR;
    }
    p->t = e;
    p->next = NULL;
    Q.rear->next = p;
    Q.rear = p;
    return OK;
}

/* 出队函数 */
/* e:出队元素 */
Status DeQueue(LinkQueue& Q, BiTree& e)
{
    // 出队,将出队元素放入e中
    QueuePtr p;
    if (Q.rear == Q.front) {
        return ERROR;
    }
    p = Q.front->next;
    e = p->t;
    Q.front->next = p->next;
    if (Q.rear == p) {
        Q.rear = Q.front;
    }
    free(p);
    return OK;
}
3. 定义结点旋转函数 —— 用于二叉树的结点平衡

LL调整(左单旋)、RR调整(右单旋)、LR调整(先左旋再右旋)、RL调整(先右旋再左旋)。

关于平衡二叉树的旋转问题请参考:平衡二叉树的构造过程图解

// 右旋处理,即LL调整
void R_Rotate(BiTree* p)
{
    BiTree L;
    L = (*p)->lchild;
    (*p)->lchild = L->rchild;
    L->rchild = *p;
    *p = L;
}

// 左旋处理,即RR调整
void L_Rotate(BiTree* p)
{
    BiTree R;
    R = (*p)->rchild;
    (*p)->rchild = R->lchild;
    R->lchild = *p;
    *p = R;
}

// 左平衡旋转处理,包括 LL 和 LR 调整
void LeftBalance(BiTree* T)
{
    BiTree L, Lr;
    L = (*T)->lchild;
    switch (L->bf)
    {
    case 1:        // 新结点插入在T的左孩子的左子树上,为LL型,作右旋处理即LL调整
        (*T)->bf = L->bf = 0;
        R_Rotate(T);
        break;
    case -1:        // 新结点插入在T的左孩子的右子树上,为LR型,作双旋处理
        Lr = L->rchild;
        switch (Lr->bf)
        {
        case 1:
            (*T)->bf = -1;
            L->bf = 0;
            break;
        case 0:
            (*T)->bf = L->bf = 0;
            break;
        case -1:
            (*T)->bf = 0;
            L->bf = 1;
            break;
        }
        Lr->bf = 0;
        L_Rotate(&(*T)->lchild);      // 先对T的左子树进行左旋处理即RR调整
        R_Rotate(T);                  // 再对T进行右旋处理即LL调整
    }
}

// 右平衡旋转处理,包括 RR 和 RL 调整
void RightBalance(BiTree* T)
{
    BiTree R, Rl;
    R = (*T)->rchild;
    switch (R->bf)
    {
    case -1:        // 新结点插入在T的右孩子的右子树上,为RR型,作左旋处理即RR调整
        (*T)->bf = R->bf = 0;
        L_Rotate(T);
        break;
    case 1:        // 新结点插入在T的右孩子的左子树上,为RL型,作双旋处理
        Rl = R->lchild;
        switch (Rl->bf)
        {
        case 1:
            (*T)->bf = 0;
            R->bf = -1;
            break;
        case 0:
            (*T)->bf = R->bf = 0;
            break;
        case -1:
            (*T)->bf = 1;
            R->bf = 0;
            break;
        }
        Rl->bf = 0;
        R_Rotate(&(*T)->rchild);      // 先对T的左子树进行左旋即RR调整
        L_Rotate(T);                  // 再对T进行右旋即LL调整
    }
}
4. 定义结点插入函数
// 若在平衡二叉树T中不存在和 e 具有相同数据的结点,则插入数据元素为 e 的新结点,
// 若因插入使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否增加,TRUE表示增加,FALSE表示没有增加

Status InsertAVL(BiTree* T, int e, Status* taller)
{
    if (!*T)
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        if (!*T) {
            return ERROR;
        }
        else {
            (*T)->data = e;
            (*T)->lchild = (*T)->rchild = NULL;
            (*T)->bf = 0;
            *taller = TRUE;
        }
    }
    else
    {
        // 树中已有和e具有相同数据的结点,则不再插入
        if (e == (*T)->data)
        {
            *taller = FALSE;
            return FALSE;
        }

        if (e < (*T)->data)    // 继续在T的左子树进行搜索
        {
            if (!InsertAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
            {
                return FALSE;
            }

            // 如果e已插入到T的左子树中,且左子树深度增加
            if (*taller)
            {
                switch ((*T)->bf)      // 检查T的平衡因子
                {
                case 1:                // 原本左子树比右子树高,再加上此结点,导致不平衡,需要作LL或LR调整
                    LeftBalance(T);
                    *taller = FALSE;
                    break;
                case 0:                // 原本左右子树等高,再加上此结点,左子树增高
                    (*T)->bf = 1;
                    *taller = TRUE;
                    break;
                case -1:                // 原本右子树比左子树高,再加上此结点,左右子树变为等高
                    (*T)->bf = 0;
                    *taller = FALSE;
                    break;
                }
            }
        }
        else        // 在T的右子树进行搜索
        {
            if (!InsertAVL(&(*T)->rchild, e, taller))
            {
                return FALSE;
            }

            if (*taller)
            {
                switch ((*T)->bf)
                {
                case 1:                // 原先左子树比右子树高,现在左右子树等高
                    (*T)->bf = 0;
                    *taller = FALSE;
                    break;
                case 0:                // 原先左右子树等高,现在右子树增高
                    (*T)->bf = -1;
                    *taller = TRUE;
                    break;
                case -1:                // 原先右子树比左子树高,现在再加上此结点,导致不平衡,需要作 RR 或 RL 调整
                    RightBalance(T);
                    *taller = FALSE;
                    break;
                }
            }
        }
    }

    return TRUE;
}
5. 定义结点删除函数
// 若在平衡二叉树T中存在和 e 具有相同数据的结点,则删除数据元素为 e 的结点,
// 若因删除使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否改变,TRUE表示改变,FALSE表示没有改变
Status DeleteAVL(BiTree* T, int e, Status* taller)
{
    if (!*T)    // 树为空,未找到删除结点
    {
        *taller = FALSE;
        return FALSE;
    }
    else
    {
        // 树中已有和e具有相同数据的结点,则不再插入
        if (e == (*T)->data)
        {
            if ((*T)->lchild != NULL && (*T)->rchild != NULL) {

            }
            return TRUE;
        }

        if (e < (*T)->data)    // 继续在T的左子树进行搜索
        {
            if (!DeleteAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
            {
                return FALSE;
            }

            // 如果e已插入到T的左子树中,且左子树深度增加
            if (*taller)
            {
                switch ((*T)->bf)      // 检查T的平衡因子
                {
                case 1:                // 原本左子树比右子树高,再删除此结点,变为等高
                    (*T)->bf = 0;
                    *taller = TRUE;
                    break;
                case 0:                // 原本左右子树等高,再删除此结点,左子树降低
                    (*T)->bf = -1;
                    *taller = FALSE;
                    break;
                case -1:                // 原本右子树比左子树高,再删除此结点,不平衡
                    RightBalance(T);
                    *taller = FALSE;
                    break;
                }
            }
        }
        else        // 在T的右子树进行搜索
        {
            if (!DeleteAVL(&(*T)->rchild, e, taller))
            {
                return FALSE;
            }

            if (*taller)
            {
                switch ((*T)->bf)
                {
                case 1:                // 原先左子树比右子树高
                    LeftBalance(T);
                    *taller = FALSE;
                    break;
                case 0:                // 原先左右子树等高,右子树变低
                    (*T)->bf = 1;
                    *taller = FALSE;
                    break;
                case -1:                // 原先右子树比左子树高,变等高
                    (*T)->bf = 0;
                    *taller = TRUE;
                    break;
                }
            }
        }
    }
    return TRUE;
}
6. 定义二叉树遍历与输出函数
/* 前序遍历 */
void PreOrder(BiTree T)
{
    if (T == NULL)
    {
        return;
    }
    //strOrderResult = strOrderResult + to_string(T->data) + " ";
    printf("%d ", T->data);
    PreOrder(T->lchild);
    PreOrder(T->rchild);
}

/* 中序遍历 */
void MidOrder(BiTree T)
{
    if (T == NULL)
    {
        return;
    }
    MidOrder(T->lchild);
    //strOrderResult = strOrderResult + to_string(T->data) + " ";
    printf("%d ", T->data);
    MidOrder(T->rchild);
}

/* 后序遍历 */
void PostOrder(BiTree T)
{
    if (T == NULL)
    {
        return;
    }
    PostOrder(T->lchild);
    PostOrder(T->rchild);
    //strOrderResult = strOrderResult + to_string(T->data) + " ";
    printf("%d ", T->data);
}

/* 层序遍历 */
void LevelOrder(BiTree root)
{
    LinkQueue Q;
    BiTree p;
    if (root != NULL) {
        InitQueue(Q);
        EnQueue(Q, root);
        while (Q.front != Q.rear) {
            DeQueue(Q, p);
            //strOrderResult = strOrderResult + to_string(T->data) + " ";
            printf("%d ", p->data);
            if (p->lchild != NULL)  EnQueue(Q, p->lchild);
            if (p->rchild != NULL)  EnQueue(Q, p->rchild);
        }
    }
}

void OutputBiTree(BiTree T)
{
    if (T != NULL) {
        printf("%d", T->data);
        if (T->lchild != NULL || T->rchild != NULL) {
            printf("(");
            OutputBiTree(T->lchild);
            if (T->rchild != NULL) {
                printf(",");
            }
            OutputBiTree(T->rchild);
            printf(")");
        }
    }
}
7. main函数测试
int main(void)
{
    int i;
    int a[10] = { 2,1,0,3,4,5,6,9,8,7 };
    BiTree T = NULL;
    Status toller;

    for (i = 0; i < 10; i++)
    {
        InsertAVL(&T, a[i], &toller);
    }
    printf("二叉树括号表示格式输出:\n");
    OutputBiTree(T);
    printf("\n");
    printf("前序遍历\n");
    PreOrder(T);
    printf("\n");
    printf("中序遍历\n");
    MidOrder(T);
    printf("\n");
    printf("后序遍历\n");
    PostOrder(T);
    printf("\n");
    printf("层序遍历\n");
    LevelOrder(T);
    printf("\n");
    return 0;
}

三、运行结果

在这里插入图片描述

四、完整代码

#include <stdio.h>
#include <stdlib.h>
//#include <string>
//using namespace std;

#define TRUE  1
#define FALSE 0
#define OK 1
#define ERROR 0

//string strOrderResult = "";         // 可用于返回多种遍历结果

// 定义平衡二叉树的结点结构
typedef struct BiTNode
{
    int data;
    int bf;             // 平衡因子
    BiTNode* lchild, * rchild;
}BiTNode, * BiTree;

typedef int Status;

/* 链队列结点结构(用于层序遍历) */
typedef struct QNode {
    BiTree t;                   // 结点数据域
    struct QNode* next;        // 结点指针域
}QNode, * QueuePtr;

/* 链队列结构(用于层序遍历) */
typedef struct {
    QueuePtr front;     // 队首指针
    QueuePtr rear;      // 队尾指针
}LinkQueue;


/* 初始化链队列函数 */
Status InitQueue(LinkQueue& Q)
{
    // 创建一个带附加头结点的空链队列
    Q.front = Q.rear = (QueuePtr)malloc(sizeof(QNode));
    if (!Q.front) {
        return ERROR;
    }
    Q.front->next = NULL;
    return OK;
}

/* 进队函数 */
/* e:插入元素 */
Status EnQueue(LinkQueue& Q, BiTree e)
{
    // 将元素e插入到链队列中
    QueuePtr p;
    p = (QueuePtr)malloc(sizeof(QNode));
    if (!p) {
        return ERROR;
    }
    p->t = e;
    p->next = NULL;
    Q.rear->next = p;
    Q.rear = p;
    return OK;
}

/* 出队函数 */
/* e:出队元素 */
Status DeQueue(LinkQueue& Q, BiTree& e)
{
    // 出队,将出队元素放入e中
    QueuePtr p;
    if (Q.rear == Q.front) {
        return ERROR;
    }
    p = Q.front->next;
    e = p->t;
    Q.front->next = p->next;
    if (Q.rear == p) {
        Q.rear = Q.front;
    }
    free(p);
    return OK;
}

// 右旋处理,即LL调整
void R_Rotate(BiTree* p)
{
    BiTree L;
    L = (*p)->lchild;
    (*p)->lchild = L->rchild;
    L->rchild = *p;
    *p = L;
}

// 左旋处理,即RR调整
void L_Rotate(BiTree* p)
{
    BiTree R;
    R = (*p)->rchild;
    (*p)->rchild = R->lchild;
    R->lchild = *p;
    *p = R;
}

// 左平衡旋转处理,包括 LL 和 LR 调整
void LeftBalance(BiTree* T)
{
    BiTree L, Lr;
    L = (*T)->lchild;
    switch (L->bf)
    {
    case 1:        // 新结点插入在T的左孩子的左子树上,为LL型,作右旋处理即LL调整
        (*T)->bf = L->bf = 0;
        R_Rotate(T);
        break;
    case -1:        // 新结点插入在T的左孩子的右子树上,为LR型,作双旋处理
        Lr = L->rchild;
        switch (Lr->bf)
        {
        case 1:
            (*T)->bf = -1;
            L->bf = 0;
            break;
        case 0:
            (*T)->bf = L->bf = 0;
            break;
        case -1:
            (*T)->bf = 0;
            L->bf = 1;
            break;
        }
        Lr->bf = 0;
        L_Rotate(&(*T)->lchild);      // 先对T的左子树进行左旋处理即RR调整
        R_Rotate(T);                  // 再对T进行右旋处理即LL调整
    }
}

// 右平衡旋转处理,包括 RR 和 RL 调整
void RightBalance(BiTree* T)
{
    BiTree R, Rl;
    R = (*T)->rchild;
    switch (R->bf)
    {
    case -1:        // 新结点插入在T的右孩子的右子树上,为RR型,作左旋处理即RR调整
        (*T)->bf = R->bf = 0;
        L_Rotate(T);
        break;
    case 1:        // 新结点插入在T的右孩子的左子树上,为RL型,作双旋处理
        Rl = R->lchild;
        switch (Rl->bf)
        {
        case 1:
            (*T)->bf = 0;
            R->bf = -1;
            break;
        case 0:
            (*T)->bf = R->bf = 0;
            break;
        case -1:
            (*T)->bf = 1;
            R->bf = 0;
            break;
        }
        Rl->bf = 0;
        R_Rotate(&(*T)->rchild);      // 先对T的左子树进行左旋即RR调整
        L_Rotate(T);                  // 再对T进行右旋即LL调整
    }
}

// 若在平衡二叉树T中不存在和 e 具有相同数据的结点,则插入数据元素为 e 的新结点,
// 若因插入使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否增加,TRUE表示增加,FALSE表示没有增加

Status InsertAVL(BiTree* T, int e, Status* taller)
{
    if (!*T)
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        if (!*T) {
            return ERROR;
        }
        else {
            (*T)->data = e;
            (*T)->lchild = (*T)->rchild = NULL;
            (*T)->bf = 0;
            *taller = TRUE;
        }
    }
    else
    {
        // 树中已有和e具有相同数据的结点,则不再插入
        if (e == (*T)->data)
        {
            *taller = FALSE;
            return FALSE;
        }

        if (e < (*T)->data)    // 继续在T的左子树进行搜索
        {
            if (!InsertAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
            {
                return FALSE;
            }

            // 如果e已插入到T的左子树中,且左子树深度增加
            if (*taller)
            {
                switch ((*T)->bf)      // 检查T的平衡因子
                {
                case 1:                // 原本左子树比右子树高,再加上此结点,导致不平衡,需要作LL或LR调整
                    LeftBalance(T);
                    *taller = FALSE;
                    break;
                case 0:                // 原本左右子树等高,再加上此结点,左子树增高
                    (*T)->bf = 1;
                    *taller = TRUE;
                    break;
                case -1:                // 原本右子树比左子树高,再加上此结点,左右子树变为等高
                    (*T)->bf = 0;
                    *taller = FALSE;
                    break;
                }
            }
        }
        else        // 在T的右子树进行搜索
        {
            if (!InsertAVL(&(*T)->rchild, e, taller))
            {
                return FALSE;
            }

            if (*taller)
            {
                switch ((*T)->bf)
                {
                case 1:                // 原先左子树比右子树高,现在左右子树等高
                    (*T)->bf = 0;
                    *taller = FALSE;
                    break;
                case 0:                // 原先左右子树等高,现在右子树增高
                    (*T)->bf = -1;
                    *taller = TRUE;
                    break;
                case -1:                // 原先右子树比左子树高,现在再加上此结点,导致不平衡,需要作 RR 或 RL 调整
                    RightBalance(T);
                    *taller = FALSE;
                    break;
                }
            }
        }
    }

    return TRUE;
}


// 若在平衡二叉树T中存在和 e 具有相同数据的结点,则删除数据元素为 e 的结点,
// 若因删除使二叉排序树失去平衡,则要作平衡调整,
// 布尔变量taller表示 T 的深度是否改变,TRUE表示改变,FALSE表示没有改变
Status DeleteAVL(BiTree* T, int e, Status* taller)
{
    if (!*T)    // 树为空,未找到删除结点
    {
        *taller = FALSE;
        return FALSE;
    }
    else
    {
        // 树中已有和e具有相同数据的结点,则不再插入
        if (e == (*T)->data)
        {
            if ((*T)->lchild != NULL && (*T)->rchild != NULL) {

            }
            return TRUE;
        }

        if (e < (*T)->data)    // 继续在T的左子树进行搜索
        {
            if (!DeleteAVL(&(*T)->lchild, e, taller)) // InsertAVL( &(*T)->lchild, e, taller )得到的是T的左孩子结点(*T)->lchild的 data,bf 等相关信息
            {
                return FALSE;
            }

            // 如果e已插入到T的左子树中,且左子树深度增加
            if (*taller)
            {
                switch ((*T)->bf)      // 检查T的平衡因子
                {
                case 1:                // 原本左子树比右子树高,再删除此结点,变为等高
                    (*T)->bf = 0;
                    *taller = TRUE;
                    break;
                case 0:                // 原本左右子树等高,再删除此结点,左子树降低
                    (*T)->bf = -1;
                    *taller = FALSE;
                    break;
                case -1:                // 原本右子树比左子树高,再删除此结点,不平衡
                    RightBalance(T);
                    *taller = FALSE;
                    break;
                }
            }
        }
        else        // 在T的右子树进行搜索
        {
            if (!DeleteAVL(&(*T)->rchild, e, taller))
            {
                return FALSE;
            }

            if (*taller)
            {
                switch ((*T)->bf)
                {
                case 1:                // 原先左子树比右子树高
                    LeftBalance(T);
                    *taller = FALSE;
                    break;
                case 0:                // 原先左右子树等高,右子树变低
                    (*T)->bf = 1;
                    *taller = FALSE;
                    break;
                case -1:                // 原先右子树比左子树高,变等高
                    (*T)->bf = 0;
                    *taller = TRUE;
                    break;
                }
            }
        }
    }
    return TRUE;
}

/* 前序遍历 */
void PreOrder(BiTree T)
{
    if (T == NULL)
    {
        return;
    }
    //strOrderResult = strOrderResult + to_string(T->data) + " ";
    printf("%d ", T->data);
    PreOrder(T->lchild);
    PreOrder(T->rchild);
}

/* 中序遍历 */
void MidOrder(BiTree T)
{
    if (T == NULL)
    {
        return;
    }
    MidOrder(T->lchild);
    //strOrderResult = strOrderResult + to_string(T->data) + " ";
    printf("%d ", T->data);
    MidOrder(T->rchild);
}

/* 后序遍历 */
void PostOrder(BiTree T)
{
    if (T == NULL)
    {
        return;
    }
    PostOrder(T->lchild);
    PostOrder(T->rchild);
    //strOrderResult = strOrderResult + to_string(T->data) + " ";
    printf("%d ", T->data);
}

/* 层序遍历 */
void LevelOrder(BiTree root)
{
    LinkQueue Q;
    BiTree p;
    if (root != NULL) {
        InitQueue(Q);
        EnQueue(Q, root);
        while (Q.front != Q.rear) {
            DeQueue(Q, p);
            //strOrderResult = strOrderResult + to_string(T->data) + " ";
            printf("%d ", p->data);
            if (p->lchild != NULL)  EnQueue(Q, p->lchild);
            if (p->rchild != NULL)  EnQueue(Q, p->rchild);
        }
    }
}

void OutputBiTree(BiTree T)
{
    if (T != NULL) {
        printf("%d", T->data);
        if (T->lchild != NULL || T->rchild != NULL) {
            printf("(");
            OutputBiTree(T->lchild);
            if (T->rchild != NULL) {
                printf(",");
            }
            OutputBiTree(T->rchild);
            printf(")");
        }
    }
}

// 对于实例,我们可以这样创建平衡二叉树
int main(void)
{
    int i;
    int a[10] = { 2,1,0,3,4,5,6,9,8,7 };
    BiTree T = NULL;
    Status toller;

    for (i = 0; i < 10; i++)
    {
        InsertAVL(&T, a[i], &toller);
    }
    printf("二叉树括号表示格式输出:\n");
    OutputBiTree(T);
    printf("\n");
    printf("前序遍历\n");
    PreOrder(T);
    printf("\n");
    printf("中序遍历\n");
    MidOrder(T);
    printf("\n");
    printf("后序遍历\n");
    PostOrder(T);
    printf("\n");
    printf("层序遍历\n");
    LevelOrder(T);
    printf("\n");
    return 0;
}
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Whitemeen太白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值