【数据结构】实验五:树和二叉树

目录

7-1 还原二叉树

参考代码

代码解析

7-2 朋友圈

参考代码

代码解析

7-3 修理牧场

参考代码

代码解析

7-4 玩转二叉树

参考代码

代码解析

7-5 根据后序和中序遍历输出先序遍历

参考代码

代码解析

7-6 完全二叉树的层序遍历

参考代码

代码解析

7-7 列出叶结点

参考代码

代码解析

7-8 部落

参考代码

代码解析

7-9 建立与遍历二叉树

参考代码

代码解析

7-10 交换二叉树中每个结点的左孩子和右孩子

参考代码

代码解析

7-11 树的遍历

参考代码

代码解析


7-1 还原二叉树

给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。

输入格式:

输入首先给出正整数N(≤50),为树中结点总数。下面两行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区别大小写)的字符串。

输出格式:

输出为一个整数,即该二叉树的高度。

输入样例:

9
ABDFGHIEC
FDHGIBEAC

输出样例:

5

参考代码

#include<stdio.h>
int max(int a,int b)
{
    return a>b?a:b;     // 三目运算符,表达式为真时 则选取冒号前的值 否则选冒号后的
}

//求二叉树的高度
int dfs(char *pre, char *in,int n){
    if(n==0)
        return 0;           //若没有结点,为空树
    int i;
    for(i=0;i<n;i++)
        if(in[i]==pre[0])
            break;                          // 找到根结点在中序的位置
    int left=dfs(pre+1,in,i);       // 递归调用dfs(),求左子树的深度
    int right=dfs(pre+i+1,in+i+1,n-i-1);    //递归调用dfs(),求右子树的深度
    return max(left,right)+1;                    //返回左右子树深度的较大值中的较大值+根结点
}

int main(){
    int n;
    scanf("%d",&n);
    char pre[n+1],in[n+1];                  //定义先序和中序
    scanf("%s%s",pre,in);
    printf("%d\n",dfs(pre,in,n));
    return 0;
}

代码解析

核心:递归调用dfs


7-2 朋友圈

某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。

输入格式:

输入的第一行包含两个正整数N(≤30000)和M(≤1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:

第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi

输出格式:

输出给出一个整数,表示在最大朋友圈中有多少人。

输入样例:

7 4
3 1 2 3
2 1 4
3 5 6 7
1 6

输出样例:

4

参考代码

#include<stdio.h>
#define Max 100000
int book[Max];
int f[Max];

// 初始化init
void init(int n)
{
    for(int i=1;i<=n;i++){
        f[i]=i;     // 设置父节点(父节点初始为自身)
        book[i]=0;  // 设置朋友数为0
    }
}

// 查询find:找到祖宗
int find(int x)
{
    if(f[x]==x)     // 递归出口,到达祖先位置,返回祖先
        return x;
    else{
        f[x]=find(f[x]);    // 不断向上查找祖先(路径亚索)
        return f[x];           // 返回父节点(祖先)
    }
}

// 合并merge/union:合并祖宗
void merge(int x,int y)
{
    int x_f = find(x);     // 找到x的祖先
    int y_f = find(y);  // 找到y的祖先
    f[x_f] = y_f;          // x的祖先指向y的祖先
}

// 找到最大朋友数
void FindMax(int n)
{
    int i;
    for(i=1;i<=n;i++){
        book[find(i)]++;    // 祖宗的朋友数+1
    }
    int ans=0;
    for(i=1;i<=n;i++){
        if(book[i]>ans)
            ans=book[i];
    }
    printf("%d",ans);   // 输出祖宗朋友数最大的
}

int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    init(n);
    // 录入数据
    for(int i=0;i<m;i++){
        int x,j;
        scanf("%d",&x);
        int a[x];       // a[x]数组作为临时数组用来存学生编号
        for(j=0;j<x;j++)
            scanf("%d",&a[j]);
        for(j=1;j<x;j++)
            merge(a[j-1],a[j]);
    }
    FindMax(n);
    return 0;
}

代码解析

并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题。一些常见的用途有求连通子图、求最小生成树的Kruskal算法和求最近公共祖先(LCA)等。

并查集的基本操作:

并查集:①初始化init→②找祖宗find→③合并祖宗merge/union

1.初始化init:

int fa[MAXN];
void init(int n){
    for (int i= 1;i<=n;++i)
        fa[i]= i;
}

2.查询find:

int find(int i){
    if(fa[i] == i) //递归出口,当到达了祖先位置,就返回祖先
        return i;
    else
        return find(fa[i]); //不断往上查找祖先
}

优化find():

int find(int i){
    if(i == fa[i])
        return i;
    else{
        fa[i] = find(fa[i]);    //该步进行了路径压缩
        return fa[i];           //返回父节点
}

3.合并union:

vo1d unionn( int i,int j){
    int i_fa = find(i);    //找到i的祖先
    int j_fa = find(j);    //找到j的祖先
    fa[i_fa] =j_fa;        //i的祖先指向j的祖先。
}

【参考资料】

超简单60秒看懂并查集_哔哩哔哩_bilibili

图论——并查集(详细版)_哔哩哔哩_bilibili


7-3 修理牧场

农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li​个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li​的总和。

但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。

请编写程序帮助农夫计算将木头锯成N块的最少花费。

输入格式:

输入首先给出正整数N(≤104),表示要将木头锯成N块。第二行给出N个正整数(≤50),表示每段木块的长度。

输出格式:

输出一个整数,即将木头锯成N块的最少花费。

输入样例:

8
4 5 1 2 1 3 1 1

输出样例:

49

参考代码

#include<stdio.h>
// 数组的两个数据交换位置
void swap(int *a,int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
// Sort排序,data[]数组最后两位为权值最小
void Sort(int data[],int n)
{
    int i;
    for(i=1;i<n;i++)
    {
        if(data[i]<data[i+1])
            swap(&data[i],&data[i+1]);
    }
    for(i=1;i<n-1;i++)
    {
        if(data[i]<data[i+1])
            swap(&data[i],&data[i+1]);
    }
}
int main()
{
    int n,data[300001],i,sum=0;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
        scanf("%d",&data[i]);
    while(n>=2)     // 循环结束条件:只剩一个结点(根节点)
    {
        Sort(data,n);
        data[n-1]=data[n-1]+data[n];    // 权值最小的两个结点合并形成新的权值结点
        sum=sum+data[n-1];              // 计算当前消费
        n--;
    }
    printf("%d",sum);
}

代码解析

本题主要采用构造最小生成树(哈夫曼树)的思想,每次找两个最小的子节点,构造父节点,最后输出根节点(Kruskal算法

哈夫曼树的构造:

给定n个权值分别为w1,w2,...,wn的结点,构造哈夫曼树:

1.将n个结点分别作为n棵仅含一个结点的二叉树,构成森林F

2.构造一个新结点,从F中选取两颗结点权值最小的树作为新结点的左、右子树,并将新节点的权值置为左、右子树上跟姐递延的权值之和

3.从F中删除刚才选出的两棵树,同时将新的到的树加入F中

4.重复步骤2和3.直至F中只剩下一棵树为止

哈夫曼树的特点:

(1)每个初始结点最终都曾为叶节点,且权值越小的结点到根节点的路径长度越大

(2)构造过程中共新建了n-1个结点(双分支结点),哈夫曼树的结点总数为2n-1

(3)每次构造都选择2棵树作为新结点的孩子,哈夫曼树中不存在度为1的结点

【参考资料】

最小生成树(Kruskal(克鲁斯卡尔)和Prim(普里姆))算法动画演示_哔哩哔哩_bilibili


7-4 玩转二叉树

给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列。所谓镜面反转,是指将所有非叶结点的左右孩子对换。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其中序遍历序列。第三行给出其前序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树反转后的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
1 2 3 4 5 6 7
4 1 3 2 6 5 7

输出样例:

4 6 1 7 5 3 2

参考代码

#include<stdio.h>
// 全局定义前序遍历序列pre[],中序遍历序列in[],层次遍历数组level[](置零)
int pre[1000000],in[1000000],n,level[1000000]={0};

// 深度优先搜索算法遍历二叉树,同时将树的数据按照一定顺序存入层次遍历数组(包含反转操作)
void dfs(int pre[],int in[],int n,int index)
{
    if(n==0)
        return;
    level[index]=pre[0];
    int i;
    for(i=0;i<n;i++)
        if(pre[0]==in[i])
            break;
    dfs(pre+1,in,i,index*2+2);      // +2表示右子树
    dfs(pre+i+1,in+i+1,n-1-i,index*2+1);    // +1表示左子树
}

int main()
{
    int num=1,i;
    scanf("%d",&n);
    for(i=0;i<n;i++)
    scanf("%d",&in[i]);
    for(i=0;i<n;i++)
    scanf("%d",&pre[i]);
    dfs(pre,in,n,0);    // 调用dfs遍历、存储
    // 输出层次遍历数组
    for(i=0;i<1000000;i++)
    {
        if(num<n && level[i]!=0)     // 循环条件:num
        {
            printf("%d ",level[i]);
            num++;
        }
        else if(level[i]!=0)
        {
            printf("%d",level[i]);
            break;
        }
    }
    return 0;
}

代码解析

结合实验7-1,采用深度优先搜索算法dfs遍历二叉树的同时通过level[]数组,层次存储结点数据。

每一层(包括本层)之前的节点个数总是上一层结点个数的两倍+1(左或右子树为空也算作在内),利用这个规律,在dfs遍历二叉树的同时,将结点数据存储到数据level[]中,最后输出level[]数组,实现层次遍历。


7-5 根据后序和中序遍历输出先序遍历

本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果。

输入格式:

第一行给出正整数N(≤30),是树中结点的个数。随后两行,每行给出N个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。

输出格式:

在一行中输出Preorder: 以及该树的先序遍历结果。数字间有1个空格,行末不得有多余空格。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:

Preorder: 4 1 3 2 6 5 7

参考代码

#include<stdio.h>
typedef struct node{
    int data;
    struct node *lchild,*rchild;
}Tree;
Tree* CreatTree(int in[],int post[],int n)
{
    if(n==0)
        return NULL;
    int i;
    Tree *tree=(Tree*)malloc(sizeof(Tree));
    tree->data=post[n-1];
    for(i=0;i<n;i++)
        if(post[n-1]==in[i])
            break;
    tree->lchild=CreatTree(in,post,i);
    tree->rchild=CreatTree(in+i+1,post+i,n-i-1);
    return tree;
}
void CountTree(Tree* tree)
{
    if(tree!=NULL)
    {
          printf(" %d",tree->data);
          CountTree(tree->lchild);
          CountTree(tree->rchild);
    }
    
}
int main()
{
    int n,i,j;
    int post[100001],in[100001];
    scanf("%d",&n);
    for(i=0;i<n;i++)
        scanf("%d",&post[i]);
    for(i=0;i<n;i++)
        scanf("%d",&in[i]);
    Tree* tree=CreatTree(in,post,n);
    printf("Preorder:");
    CountTree(tree);
}

代码解析


7-6 完全二叉树的层序遍历

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于深度为 D 的,有 N 个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 N 个结点,这样的树就是完全二叉树

给定一棵完全二叉树的后序遍历,请你给出这棵树的层序遍历结果。

输入格式:

输入在第一行中给出正整数 N(≤30),即树中结点个数。第二行给出后序遍历序列,为 N 个不超过 100 的正整数。同一行中所有数字都以空格分隔。

输出格式:

在一行中输出该树的层序遍历序列。所有数字都以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

8
91 71 2 34 10 15 55 18

输出样例:

18 34 55 71 2 10 15 91

参考代码

#include<bits/stdc++.h>
using namespace std;
int i,n,a[100];

// 生成完全二叉树遍历数组a[]
void create(int x)
{
    if(x<=n){
        create(2*x);
        create(2*x+1);
        cin>>a[x];
    }
}

int main()
{
    cin>>n;
    create(1);
    for(i=1;i<=n;i++){
        if(i!=n)
            cout<<a[i]<<" ";
        else
            cout<<a[i];
    }
    return 0;
}

代码解析

完全二叉树采用顺序存储方式,如果有左孩子,则编号为2i,如果有右孩子,编号为2i+1,然后按照后序遍历的方式(左右根),进行输入,最后顺序输出即可。


7-7 列出叶结点

对于给定的二叉树,本题要求你按从上到下、从左到右的顺序输出其所有叶节点。

输入格式:

首先第一行给出一个正整数 N(≤10),为树中结点总数。树中的结点从 0 到 N−1 编号。随后 N 行,每行给出一个对应结点左右孩子的编号。如果某个孩子不存在,则在对应位置给出 "-"。编号间以 1 个空格分隔。

输出格式:

在一行中按规定顺序输出叶节点的编号。编号间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6

输出样例:

4 1 5

参考代码

#include<bits/stdc++.h>
using namespace std;
struct Tree
{
    int f,l,r;
}s[10];
int n;
void ListLeave(int k)
{
    int flag=0;
    int a[20];
    int head=0,tail=0;
    a[tail++]=k;
    while(head<tail){
        if(s[a[head]].l!=-1)
            a[tail++]=s[a[head]].l;
        if(s[a[head]].r!=-1)
            a[tail++]=s[a[head]].r;
        if(s[a[head]].l==s[a[head]].r){
            if(flag)
                printf(" %d",a[head]);
            else
                printf("%d",a[head]);
            flag++;
        }
        head++;
    }
}
int main()
{
    char a,b;
    int i;
    cin>>n;
    for(i=0;i<n;i++)
        s[i].f=-1;
    for(i=0;i<n;i++){
            getchar();
        scanf("%c %c",&a,&b);
        if(a!='-'){
            s[i].l=a-'0';
            s[a-'0'].f=i;
        }
        else
            s[i].l=-1;
        if(b!='-'){
            s[i].r=b-'0';
            s[b-'0'].f=i;
        }
        else
            s[i].r=-1;
    }
    for(i=0;i<n;i++){
        if(s[i].f==-1)
            ListLeave(i);
    }
}

代码解析


7-8 部落

在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈。我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是否属于同一个部落。

输入格式:

输入在第一行给出一个正整数N(≤104),是已知小圈子的个数。随后N行,每行按下列格式给出一个小圈子里的人:

K P[1] P[2] ⋯ P[K]

其中K是小圈子里的人数,P[i](i=1,⋯,K)是小圈子里每个人的编号。这里所有人的编号从1开始连续编号,最大编号不会超过104。

之后一行给出一个非负整数Q(≤104),是查询次数。随后Q行,每行给出一对被查询的人的编号。

输出格式:

首先在一行中输出这个社区的总人数、以及互不相交的部落的个数。随后对每一次查询,如果他们属于同一个部落,则在一行中输出Y,否则输出N

输入样例:

4
3 10 1 2
2 3 4
4 1 5 7 8
3 9 6 4
2
10 5
3 7

输出样例:

10 2
Y
N

参考代码

#include<stdio.h>
#define Max 100000
int book[Max];
int f[Max]; // 祖宗结点数组

// 初始化init
void init(int n)
{
    for(int i=1;i<=n;i++){
        f[i]=i;     // 设置父节点(父节点初始为自身)
        book[i]=0;  // 设置人数为0
    }
}

// 查询find:找到祖宗
int find(int x)
{
    if(f[x]==x)     // 递归出口,到达祖先位置,返回祖先
        return x;
    else{
        f[x]=find(f[x]);    // 不断向上查找祖先(路径亚索)
        return f[x];           // 返回父节点(祖先)
    }
}

// 合并merge/union:合并祖宗
void merge(int x,int y)
{
    int x_f = find(x);     // 找到x的祖先
    int y_f = find(y);  // 找到y的祖先
    f[x_f] = y_f;          // x的祖先指向y的祖先
}

// 找到最大朋友数
void FindTribe(int n)
{
    int tribe[Max]={0};
    int num_per=n,num_tri=0;
    for(int i=1;i<=n;i++){
        if(tribe[find(i)] == 0){
            tribe[find(i)] = 1;
            num_tri++;
        }
    }
    printf("%d %d\n",num_per,num_tri);   // 输出祖宗朋友数最大的
}

void judge(int a, int b){
    if(find(a) == find(b))
        printf("Y\n");
    else
        printf("N\n");
}

int main()
{
    int m;
    int num=0;  // 用于统计成员数量,初始为0
    scanf("%d",&m);
    init(Max);
    // 录入数据
    for(int i=0;i<m;i++){
        int x,j;
        scanf("%d",&x);
        int a[x];       // a[x]数组作为临时数组用来存学生编号
        for(j=0;j<x;j++){
            scanf("%d",&a[j]);
            if(book[a[j]]==0){
                book[a[j]] = 1;
                num++;
            }
        }
        for(j=1;j<x;j++)
            merge(a[j-1],a[j]);
    }
    FindTribe(num);
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++){
        int a,b;
        scanf("%d %d",&a, &b);
        judge(a,b);
    }
    return 0;
}

代码解析

同样是并查集(Union-find Sets)问题,可参考7-2朋友圈,注意设置数组计算人数和部落数量。


7-9 建立与遍历二叉树

以字符串的形式定义一棵二叉树的先序序列,若字符是‘#’, 表示该二叉树是空树,否则该字符是相应结点的数据元素。读入相应先序序列,建立二叉链式存储结构的二叉树,然后中序遍历该二叉树并输出结点数据。

输入格式:

字符串形式的先序序列(即结点的数据类型为单个字符)

输出格式:

中序遍历结果

输入样例:

在这里给出一组输入。例如:

ABC##DE#G##F###

输出样例:

在这里给出相应的输出。例如:

CBEGDFA

参考代码

#include<bits/stdc++.h>
struct node
{
    char data;
    struct node* l;
    struct node* r;
};
void Creat(struct node* *T)
{
    char ch=getchar();
    if(ch=='#')
        *T=NULL;
    else{
        *T=(struct node*)malloc(sizeof(struct node));
        (*T)->data=ch;
        Creat(&((*T)->l));
        Creat(&((*T)->r));
    }
}
void Show(struct node* BT)
{
    if(BT){
        Show(BT->l);
        printf("%c",BT->data);
        Show(BT->r);
    }
}
int main()
{
    struct node* T;
    Creat(&T);
    Show(T);
    return 0;
}


代码解析


7-10 交换二叉树中每个结点的左孩子和右孩子

以二叉链表作为二叉树的存储结构,交换二叉树中每个结点的左孩子和右孩子。

输入格式:

输入二叉树的先序序列。

提示:一棵二叉树的先序序列是一个字符串,若字符是‘#’,表示该二叉树是空树,否则该字符是相应结点的数据元素。

输出格式:

输出有两行:

第一行是原二叉树的中序遍历序列;

第二行是交换后的二叉树的中序遍历序列。

输入样例:

ABC##DE#G##F###

输出样例:

CBEGDFA

AFDGEBC

参考代码

#include<bits/stdc++.h>
struct node
{
    char data;
    struct node* l;
    struct node* r;
};
void Creat(struct node* *T)
{
    char ch=getchar();
    if(ch=='#')
        *T=NULL;
    else{
        *T=(struct node*)malloc(sizeof(struct node));
        (*T)->data=ch;
        Creat(&((*T)->l));
        Creat(&((*T)->r));
    }
}
void Show(struct node* BT)
{
    if(BT){
        Show(BT->l);
        printf("%c",BT->data);
        Show(BT->r);
    }
}
void Change(struct node* *T)
{
    if((*T)->l||(*T)->r){
        struct node* temp;
        temp=(*T)->l;
        (*T)->l=(*T)->r;
        (*T)->r=temp;
        if((*T)->l)
            Change(&((*T)->l));
        if((*T)->r)
            Change(&((*T)->r));
    }
}
int main()
{
    struct node* T;
    Creat(&T);
    Show(T);
    printf("\n");
    Change(&T);
    Show(T);
    return 0;
}

代码解析


7-11 树的遍历

给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。

输入格式:

输入第一行给出一个正整数N(≤30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。

输出格式:

在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例:

4 1 6 3 5 7 2

参考代码

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

struct node{
    int data;
    struct node *l;
    struct node *r;
};

struct node* SetTree(int a[],int b[],int len){
    struct node* BT;
    int i;
    if(len==0)
        return NULL;
    else{
        BT=(struct node*)malloc(sizeof(struct node));
        BT->data=a[len-1];
        for(i=0;i<len;i++){
            if(b[i]==a[len-1])
                break;
        }
        BT->l=SetTree(a,b,i);
        BT->r=SetTree(a+i,b+i+1,len-1-i);
    }
    return BT;
}

void cengXu(struct node* BT){
    struct node* p[1200];
    int in,out,flag;
    in=out=0;
    flag=1;
    p[in++]=BT;
    while(in>out){
        if(p[out]){
           if(flag){
               printf("%d",p[out]->data);
               flag=0;
           }
           else
               printf(" %d",p[out]->data);
           p[in++]=p[out]->l;
           p[in++]=p[out]->r;
        }
        out++;
    }
}

int main(){
    int n,i;
    scanf("%d",&n);
    int f1[n],f2[n];
    for(i=0;i<n;i++)
        scanf("%d",&f1[i]);
    for(i=0;i<n;i++)
        scanf("%d",&f2[i]);
    struct node* BT = SetTree(f1,f2,n);
    cengXu(BT);
    return 0;
}

代码解析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值