图的常考代码

        如下为本人自己总结整理的常考的图的数据结构伪代码(C语言),可能存在错误,欢迎批评指正。

目录

1、基础题目

●邻接矩阵储存结构定义

●邻接表存储结构定义

●将无向/有向图邻接矩阵存储转化为邻接表存储

●将无向/有向图邻接表存储转化为邻接矩阵存储

●基于邻接矩阵存储的图,给定边两端结点添加边

●基于邻接矩阵存储的图,删除指定节点的一条边

●基于邻接表存储的图,给定边两端结点添加边

●基于邻接表存储的图,删除指定节点的一条边(略)

2、重点题目:DFS/BFS、BFS求无权图的单源最短路径、DFS实现(逆)拓扑排序、遍历判断图连通性、判断图中是否有环、遍历计算图的连通分量数、求度

●基于邻接矩阵的带权无向/有向图的递归深度优先搜索DFS

●基于邻接表的带权无向/有向图的递归深度优先搜索DFS

●基于邻接矩阵的带权无向/有向图的非递归深度优先搜索DFS

●基于邻接表的带权无向/有向图的非递归借助栈深度优先搜索DFS

●基于邻接矩阵+邻接表的带权无向/有向图的递归广度优先搜索BFS

●基于邻接矩阵+邻接表的带权无向/有向图的非递归借助队列广度优先搜索BFS

●BFS求无权图的单源最短路径

●基于邻接矩阵存储,每个顶点的入度存于数组indegree中,(借助队列非递归)对带权+无权有向图进行拓扑排序

●基于邻接表存储,每个顶点的入度存于数组indegree中,(借助队列非递归)对有向图进行拓扑排序

●基于邻接矩阵存储,应用DFS判断带权+无权、无向/有向图的连通性

●基于邻接矩阵存储,应用BFS判断带权+无权无向/有向图的连通性

●基于邻接矩阵存储,求有向/无向图各顶点的度

●基于邻接表存储,求有向/无向图各顶点的入度、出度、度

3、拓展题目:判断图为树、判断/输出两点路径

●基于邻接矩阵存储,借助DFS判断无向图G是否为一棵树(仅供参考)

●基于邻接表存储,借助DFS判断无向图G是否为一棵树

●基于深度优先or广度优先判断以邻接表形式存储的有向图中是否有顶点vi到vj的路径

●采用邻接表存储结构,编写一个判别无向图中任意给定的两个节点之间是否存在一条长度为k的简单路径的算法。注:简单路径是指其顶点序列不含重现的顶点。(2017 杭州电子科技大学 851)

●基于邻接表存储,输出从顶点vi到vj的所有简单路径


1、基础题目

●邻接矩阵储存结构定义
#define MAXVEX 100//最大顶点数
typedef char VetType;//顶点类型
typedef int EdgeType;//边权值类型
typedef struct{
    VetType vex[MAXVEX];//定义顶点数组
    EdgeType arc[MAXVEX][MAXVEX];//定义邻接矩阵,数据元素为边权值类型
    int numV,numE;//图中顶点数、边数
}MGraph;//以邻接矩阵存储的图类型:图类的声明

ps:arc表示弧,edge表示边

●邻接表存储结构定义
#define MAXVEX 100
typedef char VexType;//顶点类型
typedef int EdgeType;//边权值类型
typedef struct ENode{//边表结点:ENode或ANode
    int adjvex;//邻接点域,存储该顶点对应的下标
    EdgeType weight;//边权值
    struct ENode *next;//链域
}ENode;
typedef struct VNode{//顶点表结点
    VexType data;//数据域,存储顶点信息
    ENode *first;//指针域,指向第一条依附于该顶点的边或弧的指针
}VNode;
typedef struct{
    VNode adjList[MAXVEX];//邻接表
    int numV,numE;//图中顶点数、边数(弧数numA)
}ALGraph;//以邻接表存储的图类型:图类的声明
●将无向/有向图邻接矩阵存储转化为邻接表存储
/* 邻接矩阵存储结构 */
#define MAXVEX 100
typedef char VexType;//顶点类型
typedef int EdgeType;//边权值类型
typedef struct {
    VexType vex[MAXVEX];//顶点表
    EdgeType edge[MAXVEX][MAXVEX];//邻接矩阵,数据元素为边权值类型
    int numV,numE;//图中顶点数、边数
}MGraph;

void MGraphToALGraph(MGraph &g,ALGraph &G){
    G=(ALGraph*)malloc(sizeof(ALGraph));//①为邻接表分配内存空间
    for(int i=0;i<g.numV;i++){//②将顶点表的指针域置空
        G.adjList[i].first=NULL;//指针域
        G.adjList[i].data=g.vex[i];//数据域
    }
    ENode *p;//指向边表结点
    for(int i=0;i<g.numV;i++){
        for(int j=g.numV-1;j>=0;j--){//邻接矩阵下标从0开始
            if(g.edge[i][j]!=0){
                p=(ENode*)malloc(sizeof(ENode));//开辟边表结点空间
                p→adjvex=j;
                p→next=G.adjList[i].first;//头插法
                G.adjList[i].first=p;
                //p→weight=g.edge[i][j]
            }
        }
    }
    G.numV=g.numV;
    G.numE=g.numE;
}
●将无向/有向图邻接表存储转化为邻接矩阵存储
/* 无向图的邻接表存储结构 */
#define MAXVEX 100
typedef char VexType;//顶点类型
typedef int EdgeType;//边权值类型
typedef struct ENode{//边表结点
    int adjvex;//邻接点域,存储该结点对应的下标
    EdgeType weight;//边权值
    struct ENode*next;//链域
}ENode;
typedef struct VNode{//顶点表结点
    VexType data;//数据域
    ENode *frist;//指针域
}VNode;
typedef struct{
    VNode adjList[MAXVEX];//邻接表
    int numV,numE;//图中顶点数,边数
}ALGraph;

void ALGraphToMGraph(ALGraph&G,EdgeType edge[M][N]){//EdgeType为边权值类型
    ENode *p;
    for(int i=0;i<G.numV;i++){//遍历每个顶点表结点指针域指向的边链表
        p=G.adjList[i].first;//取顶点i的第一个出边
        while(p!=NULL){
            edge[i][p→adjvex]=edge[p→adjvex][i]=p→weight;//不带权图为1
            p=p→next;
        }
    }
}
●基于邻接矩阵存储的图,给定边两端结点添加边

//无向/有向带权图邻接矩阵

//无向/有向带权图邻接矩阵
void InsertEdge(MGraph &g,VexType v1,VexType v2,EdgeType w){//VexType为顶点类型,        EdgeType为边权值类型
    int a,b;//记录顶点v1和v2的下标
    for(int i=0;i<g.numV;i++){//numV为图当前顶点数
        if(g.vex[i]==v1)a=i;
        if(g.vex[i]==v2)b=i;
    }
    g.edge[a][b]=g.edge[b][a]=w;//无权图=1;有向图只改变v1→v2的边权值
    g.numE++;//图中边数加一
}
●基于邻接矩阵存储的图,删除指定节点的一条边
//无向/有向带权图邻接矩阵
void DeEdge(MGraph &g,VexType v1,VexType v2){//VexType为顶点类型
    int a,b;//记录顶点v1和v2的下标
    for(int i=0;i<g.numV;i++){//numV为图当前顶点数
        if(g.vex[i]==v1)a=i;
        if(g.vex[i]==v2)b=i;
    }
    g.edge[a][b]=g.edge[b][a]=0;//有向图只改变v1→v2的边权值
    g.numE--;//图中边数减一
}
基于邻接表存储的图,给定边两端结点添加边
//有向带权图邻接表
vord InsertEdge(AlGraph &G,int v1,int v2,EdgeType w){//头插法
    ENode *s=(ENode*)malloc(sizeof(ENode));
    s→weight=w;
    s→adjvex=v2;
    s→next=G.adjList[v1].first;
    G.adjList[v1].first=s;
}

vord InsertEdge(AlGraph &G,int v1,int v2,EdgeType w){//尾插法
    ENode *p;
    ENode *s=(ENode*)malloc(sizeof(ENode));
    s→weight=w;
    s→adjvex=v2;
    s→next=NULL;
    if(G.adjList[v1].first==NULL)
        G.adjList[v1].first=s;
    else {
        p=G.adjList[v1].first;
        while(p→next!=NULL)p=p→next;
        p→next=s;
    }
}
●基于邻接表存储的图,删除指定节点的一条边(略)

2、重点题目:DFS/BFS、BFS求无权图的单源最短路径、DFS实现(逆)拓扑排序、遍历判断图连通性、判断图中是否有环、遍历计算图的连通分量数、求度

总结:①无权图的权值为0或1,带权图的权值为w或∞

②基于邻接矩阵存储时,要注意有向/无向图的矩阵对称性

③邻接表和邻接矩阵的不同点在于for循环的不同:

邻接矩阵:for(int i=0;i<g.numV;i++){for(int j=0;j<g.numV;j++){...}}+g.edge[i][j]!=0||g.edge[i][j]!=∞

邻接表for(int w=firstNeighbor(g,v);w>=0;w=nextNeighbor(g,v,w)){...}

④DFS借助栈实现深度,BFS借助队列实现广度

⑤区分有向图和无向图:G→kind==DG表示为有向图,G→kind==UDG表示为无向图

●基于邻接矩阵带权无向/有向图递归深度优先搜索DFS

ps:有向图和无向图的区分在创建图+添加边+删除边方法中,遍历中不影响

#define MAXVEX 100
int visited[MAXVEX]={0};//定义并初始化标记数组
void DFS(MGraph g,int v){//从顶点v开始
    visit[v];//即print(“%c ”,g.vex[v]);
    visited[v]=1;
    for(int i=0;i<g.numV;i++){
        if(visited[i]==0&&g.edge[v][i]!=0)
            DFS(g,i);//递归实现:v→i→其他顶点
    }
}

ps:若已知顶点元素值为VexType v,要先找到其顶点下标

void DFSTraverse(MGraph g){//深度优先遍历过程
    for(int i=0;i<g.numV;i++){
        if(visited[i]==0)
            DFS(G,i);//如果下标为i对应的顶点未被访问过,则从此开始做深度优先遍历
    }
}
基于邻接表的带权无向/有向图的递归深度优先搜索DFS
/* 获得与顶点v相邻的第一个结点的下标 */
int firstNeighbor(ALGraph G,int v){
    if(G.adjList[v].first!=NULL)
        return G.adjList[v].first.adjvex;
    return -1;
}
/* 获得顶点v除w以外的下一个相邻结点的下标 */
int nextNeighbor(ALGraph G,int v,int w){
    ENode *p=G.adjList[v].first;
    while(p!=NULL&&p→data!=w)p=p→next;
    if(p!=NULL&&p→next!=NULL)return p→next→adjvex;
    else return -1;
}
#define MAXVEX 100
int visited[MAXVEX]={0};//定义并初始化标记数组
    void DFS(ALGraph G,int v){
    visit(v);//访问v
    visited[v]=1;//已访问顶点v
    for(int w=firstNeighbor(G,v);w>=0;w=nextNeighbor(G,v,w)){
        if(visited[w]==0)DFS(G,w);
    }
}

void DFSTraverse(ALGraph G){
    for(int i=0;i<G.numV;i++){
        if(visited[i]==0)DFS(G,i);
    }
}
基于邻接矩阵的带权无向/有向图的非递归深度优先搜索DFS
#define MAXVEX 100
int visited[MAXVEX]={0};//①定义并初始化标记数组
    void DFS(MGraph g,int v){
    Stack S;//定义栈
    Push(S,v);//②顶点下标v压入栈中(若让顶点入栈,后面边中存的是下标,不好整)
    visited[v]=1;
    while(!isEmpty(S)){//③栈非空出栈
        Pop(S,v);
        visit(v);
        for(int i=0;i<g.numV;i++){
            if(visited[i]==0 && g.edge[v][i]!=0){//④未进过栈的进栈并存在边的入栈
                Push(S,i);
                visited[i]=1;
            }
        }
    }
}
基于邻接表的带权无向/有向图的非递归借助栈深度优先搜索DFS
#define MAXVEX 100
int visited[MAXVEX]={0};//定义并初始化标记数组
    void DFS(ALGraph G,int v){
    Stack S;//定义栈
    Push(S,v);//顶点下标v压入栈中(若让顶点入栈,后面边中存的是下标,不好整)
    visited[v]=1;
    while(!isEmpty(S)){//栈非空
        Pop(S,v);
        visit(v);
        for(int w=firstNeighbor(G,v);w>=0;w=nextNeighbor(G,v,w)){
            if(visited[w]==0){//未进过栈的进栈
                Push(S,w);
                visited[w]=1;
            }
        }
    }
}

void DFSTraverse(ALGraph G){//可省略,因为可能前面的DFS算法对于连通图来讲执行一次就够了
    //int m=0;//记录调用DFS算法的次数=图中连通分类的个数
    for(int i=0;i<G.numV;i++){
        if(visited[i]==0){
            DFS(G,i);
            //m++;//m=调用DFS算法的次数=图中连通分类的个数
        }
    }
}
基于邻接矩阵+邻接表的带权无向/有向图的递归广度优先搜索BFS

        BFS不像深度优先搜索那样有回退的情况,故BFS不是一个递归的算法,为了实现逐层访问,算法必须借助一个辅助队列,以记忆正在访问的顶点的下一层顶点。

●基于邻接矩阵+邻接表带权无向/有向图非递归借助队列广度优先搜索BFS
#define MAXVEX 100
typedef char VexType;
int visited[MAXVEX]={0};//①定义并初始化标记数组
void BFS(MGraph g,int v){
    visit(v);//与DFS不同,此处不可移位!
    visited[v]=1;
    EnQueue(Q,v);//顶点下标v入队列
    while(!isEmpty(Q)){//③队列不为空、出队
        DeQueue(Q,v);//队头元素出队赋值给v
        for(int j=0;j<g.numV;j++){//基于邻接矩阵存储
            if(visit[j]==0&&g.edge[v][j]!=0){//④未访问过且与v存在边的顶点入队访问
                visit(j);
                visited[j]=1;
                EnQueue(Q,j);
            }
        }
        /*基于邻接表存储
        for(int w=firstNeighbor(G,v);w>=0;w=nextNeighbor(G,v,w)){
            if(visited[w]==0){
                visit(w);
                visited[w]=1;
                EnQueue(Q,w);
            }
        }
        */
    }
}

void BFSTraverse(MGraph g){
    for(int i=0;i<g.numV;i++){
        if(visited[i]==0)//②未访问过的顶点入队
            BFS(G,i);
    }
}
●BFS求无权图的单源最短路径
void BFS_MIN_Distance(ALGraph G,int v){
    for(int i=0;i<G.numV;++i){
        d[i]=∞;//初始化距离数组,表示从v到i结点的最短路径
        //path[i]=-1;//path数组保存最短路径上的前缀顶点
    }
    visited[v]=1;
    d[v]=0;//顶点v到自身的距离为0
    EnQueue(Q,v);
    while(!isEmpty(Q)){
        DeQueue(Q,v);//队头元素出队并赋值给v 
        for(int w=firstNeighbor(G,v);w>=0;w=nextNeighbor(G,v,w)){
            if(visited[w]==0){
                visited[w]=1;
                d[w]=d[v]+1;
                EnQueue(Q,w);
                //path[w]=v;
            }
        }
    }
}
●基于邻接矩阵存储,每个顶点的入度存于数组indegree中,(借助队列非递归)对带权+无权有向图进行拓扑排序
bool ToPoSort(MGraph g,int indegree[]){
    Queue Q;//Stack S;将队列直接换为栈毫无问题
    InitQueue(Q);
    int m=0,v;//变量m统计已输出点的个数
    for(int i=0;i<g.numV;i++){//第一轮入度为0的结点入队
        if(indegree[i]==0)EnQueue(Q,i);//顶点下标入队列而不是顶点本身入队列
    }
    while(!isEmpty(Q)){//队列不为空
        DeQueue(Q,v);//队头元素出队后赋值给v
        m++;//出队时+1即可,入队时可不加
        visit(t);
        for(int i=0;i<g.numV;i++){//如果t与i间存在边,则删除边、度-1,度=0的顶点入队
            if(g.edge[v][i]!=0||g.edge[v][i]!=∞){
                g.edge[v][i]=0;//与BFS判断连通性的区别,有此语句则不需要visited数组
                indegree[i]--;
                if(indegree[i]==0)EnQueue(Q,i);
            }
        }
    }
    if(m<g.numV)return false;//图中存在剩余的点,不能进行拓扑排序
    else return ture;
}
●基于邻接表存储,每个顶点的入度存于数组indegree中,(借助队列非递归)对有向图进行拓扑排序

                   return ture;

}

ps:(判断连通性的两种方法)①DFS/BFS如果visited[]数组均为ture则说明图为连通图;否则DFSTraverse/BFSTraverse算法中DFS/BFS执行了几次就有几个连通分量

②在从一点开始执行一次DFS/BFS算法时添加变量m统计出队or出栈时等的结点数,若m<g.numV则不连通,否则连通(判断连通性时需要定义visited数组,拓扑排序时不用)

●基于邻接矩阵存储,应用DFS判断带权+无权、无向/有向图的连通性
#define MAXVEX 100
int visited[MAXVEX]={0};//定义并初始化标记数组
void DFS(MGraph g,int v){//默认顶点类型为int
    visit(v);
    visited[v]=1;
    for(int i=0;i<g.numV;i++){//基于邻接矩阵
        if(visited[i]==0&&(edge[v][i]!=0||edge[v][i]!=∞))
            DFS(g,i);
    }
    /*基于邻接表
    for(int w=firstNeighbor(g,v);w>=0;w=nextNeighbor(g,v,w)){
        if(visited[w]==0)DFS(g,w);
    }
    */
}

bool dfs_solve(MGraph g){//法一
    DFS(g,0);//从0开始进行深度优先遍历
    for(int i=0;i<g.numV;i++){
        if(visited[i]==0)return false;
    }
    return ture;
}
●基于邻接矩阵存储,应用BFS判断带权+无权无向/有向图的连通性
#define MAXVEX 100
int visited[MAXVEX]={0};
bool bfs_solve(MGraph g){
    Queue Q;
    InitQueue(Q);
    EnQueue(Q,0);
    visited[0]=1;
    int v,m=0;//统计已访问顶点个数
    while(!isEmpty(Q)){
        DeQueue(Q,v);//队头元素出队后赋值给v
        m++;
        for(int i=0;i<g.numV;i++){//基于邻接矩阵
            if(visited[i]==0&&(g.edge[t][i]!=0||g.edge[t][i]!=∞)){
                EnQueue(Q,i);
                visited[i]=1;
            }
        }
        /*基于邻接表
        for(int w=firstNeighbor(g,t);w>=0;w=nextNeighbor(g,t,w)){
            if(visited[w]==0){
                EnQueue(Q,w);
                visited[w]=1;
            }
        }
        */
    }
    if(m<g.numV)return false;
    else return ture;
}
●基于邻接矩阵存储,求有向/无向图各顶点的度
void Degree(MGraph g,int &d[]){//数组d存储各顶点的度
    for(int i=0;i<g.numV;i++)d[i]=0;
    for(int i=0;i<g.numV;i++){
        for(int j=0;j<g.numV;j++){
            if(g.edge[i][j]!=0)d[i]++;
        }
    }
}
●基于邻接表存储,求有向/无向图各顶点的入度、出度、度
void Degree(ALGraph G,int &d1[],int &d2[]){
    for(int i=0;i<G.numV;i++){
        d1[i]=0;//记录出度
        d2[i]=0;//记录入度
    }
    for(int i=0;i<G.numV;i++){//出度
        for(int w=firstNeighbor(G,i);w>=0;w=nextNeighbor(G,i,w)){
            d1[i]++;
        }
    }
    for(int i=0;i<G.numV;i++){//出度
        for(int w=firstNeighbor(G,i);w>=0;w=nextNeighbor(G,i,w)){
            d2[w]++;
        }
    }
}

3、拓展题目:判断图为树、判断/输出两点路径

●基于邻接矩阵存储,借助DFS判断无向图G是否为一棵树(仅供参考)
#define MAXVEX 100
int visited[MAXVEX]={0};//定义并初始化标记数组
void DFS(MGraph G,int v,int visited[],int &Vn,int &En){
    visited[v];//已访问
    Vn++;
    for(int i=0;i<G.numV;i++){
        if(edge[v][i]!=0)En++;
        if(visited[i]==0&edge[v][i]!=0)
            DFS(G,w,visited,Vn,En);
        }
    }
    bool isTree(MGraph G){//与基于邻接表的算法基本相同
    int Vn=0,En=0;//连通子图的顶点数、边数
    DFS(G,i,visited,Vn,En);
    if(Vn==G.numV&&En=2*(G.numV-1))//无向图要*2,有向图
        return ture;
    return false;
}
基于邻接表存储,借助DFS判断无向图G是否为一棵树
#define MAXVEX 100
int visited[MAXVEX]={0};//定义并初始化标记数组
void DFS(ALGraph G,int v,int visited[],int &Vn,int &En){
    visited[v];//已访问
    Vn++;
    for(int w=firstNeighbor(G,v);w>=0;w=nextNeighbor(G,v,w)){
        En++;
        if(visited[w]==0)DFS(G,w,visited,Vn,En);
        }
}

bool isTree(ALGraph G){
    int Vn=0,En=0;//连通子图的顶点数、边数
    DFS(G,i,visited,Vn,En);
    if(Vn==G.numV&&En=2*(G.numV-1))//无向图要*2,有向图
        return ture;
    return false;
}

ps:(n个顶点则图的最小生成树有n-条边);借助BFS也可;

判断是否为一棵树,只能是无向图判断∵无箭头

●基于深度优先or广度优先判断以邻接表形式存储的有向图中是否有顶点vi到vj的路径
#define MAXVEX 100
int visited[MAXVEX]={0};
//出发点为i,终点为j
void exist_path_DFS(ALGraph G,int i,int j,bool &canReach){
    if(i==j){
        canReach=ture;
        return;
    }
    visited[i]=1;
    for(int w=firstNeighbor(G,i);w>=0;w=nextNeighbor(G,i,w)){
        if(visited[w]==0 && canReach==false)
            exist_path_DFS(G,w,j,canReach);
    }
    visited[i]=0;/*这里需要把已经访问的点重新置为0,因为如果当前不存在长度为k到达j点,那么这个点还是可以使用的,因为有可能从其他点出发可以到达j点并且长度为k*/
    return 0;
}

#define MAXVEX 100
typedef char VexType;
int visited[MAXVEX]={0};//①初始化标记数组
void exist_path_BFS(ALGraph G){
    int t;
    Queue Q;//定义队列
    InitQueue(Q);
    visited[i]=1;
    EnQueue(Q,i);//②顶点下标i入栈
    while(!isEmpty(Q)){//③队列不为空、出队
        DeQueue(Q,t);//队头元素出队赋值给t
        if(t==j)return 1;
        for(int w=firstNeighbor(G,t);w>=0;w=nextNeighbor(G,,t,w)){
            if(p==j)return 1;
            if(visited[w]==0){
                visited[w]=1;
                EnQueue(Q,w);
            }
        }
    }
}
●采用邻接表存储结构,编写一个判别无向图中任意给定的两个节点之间是否存在一条长度为k的简单路径的算法。注:简单路径是指其顶点序列不含重现的顶点。(2017 杭州电子科技大学 851)
#define MAXVEX 100
int visited[MAXVEX]
//出发点为i,终点为j,长度为k
int exist_path_len(ALGraph G,int i,int j,int k){
    if(i==j&&k==0)return 1; 
    else if(k>0){
        visited[i]=1;
        for(int w=firstNeighbor(G,i);w>=0;w=nextNeighbor(G,i,w)){
            if(visited[w]==0 && exist_path_len(G,w,j,k-1))return 1;
        }
        visited[i]=0;/*这里需要把已经访问的点重新置为0,因为如果当前不存在长度为k到达j点,那么这个点还是可以使用的,因为有可能从其他点出发可以到达j点并且长度为k*/ 
    }
    return 0;
}
●基于邻接表存储,输出从顶点vi到vj的所有简单路径
#define MAXVEX 100
int visited[MAXVEX]={0};
void FindPath_DFS(ALGraph G,int i,int j,int path[],int d){
    d++;
    path[d]=i;
    visited[i]=1;
    if(i==j)print(path[]);//找到一条路径输出
    for(int w=firstNeighbor(G,i);w>=0;w=nextNeighbor(G,i,w)){
        if(visit[w]==0)FindPath_DFS(G,w,j,path,d);
    }
    visited[i]=0;//恢复环境,使该顶点可以重新使用
}
  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值