数据结构基础--图

数据结构基础–图(14)

图的定义:

图 G 由顶点集 V 和边集 E 组成,记为 G = (V, E),其中 V (G) 表示图 G 中顶点的有限非空集;E (G) 表示图 G 中顶点之间的关系(边)集合。若 V = {v₁, v₂, …, vₙ},则用 | V | 表示图 G 中顶点的个数,也称图 G 的阶,E = {(u, v) | u∈V, v∈V},用 | E | 表示图 G 中边的条数。

注意:线性表可以是空表,树可以是空树,但图不可以是空,即 V 一定是非空集

  • 图的应用:地铁图,公交图

图的类型:

无向图

有向图

简单图

多重图

无向图的定义:

在这里插入图片描述

若 E 是无向边(简称边)的有限集合时,则图 G 为无向图。边是顶点的无序对,记为(v, w)或(w, v),因为**(v, w) = (w, v)**,其中 v、w 是顶点。可以说顶点 w 和顶点 v 互为邻接点。边(v, w)依附于顶点 w 和 v,或者说边(v, w)和顶点 v、w 相关联。

G 2 = ( V 2 , E 2 ) G_2 = (V_2, E_2) G2=(V2,E2)

V 2 = { A , B , C , D , E } V_2 = \{A, B, C, D, E\} V2={A,B,C,D,E}

E 2 = { ( A , B ) , ( B , D ) , ( B , E ) , ( C , D ) , ( C , E ) , ( D , E ) } E_2 = \{(A, B), (B, D), (B, E), (C, D), (C, E), (D, E)\} E2={(A,B),(B,D),(B,E),(C,D),(C,E),(D,E)}

有向图的定义:

在这里插入图片描述

若 E 是有向边(也称弧)的有限集合时,则图 G 为有向图。弧是顶点的有序对,记为 <v, w>,其中 v、w 是顶点,v 称为弧尾,w 称为弧头,<v, w > 称为从顶点 v 到顶点 w 的弧,也称 v 邻接到 w,或 w 邻接自 v。<v, w>≠<w, v>

G 1 = ( V 1 , E 1 ) G_1 = (V_1, E_1) G1=(V1,E1)

V 1 = { A , B , C , D , E } V_1 = \{A, B, C, D, E\} V1={A,B,C,D,E}

E 1 = { < A , B > , < A , C > , < A , D > , < A , E > , < B , A > , < B , C > , < B , E > , < C , D > } E_1 = \{<A, B>, <A, C>, <A, D>, <A, E>, <B, A>, <B, C>, <B, E>, <C, D>\} E1={<A,B>,<A,C>,<A,D>,<A,E>,<B,A>,<B,C>,<B,E>,<C,D>}

简单图的定义:

  1. 不存在重复边
  2. 不存在顶点到自身的边

多重图的定义:

图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联,则G为多重图

顶点的度、入度、出度

  • 对于无向图:

顶点 v 的度是指依附于该顶点的边的条数,记为 TD (v)。

在具有 n 个顶点、e 条边的无向图中, ∑ i = 1 n T D ( v i ) = 2 e \sum_{i = 1}^{n}TD(v_i)=2e i=1nTD(vi)=2e

即无向图的全部顶点的度的和等于边数的 2 倍

  • 对于有向图:

入度是以顶点 v 为终点的有向边的数目,记为 ID (v)

出度是以顶点 v 为起点的有向边的数目,记为 OD (v)。

顶点 v 的度等于其入度和出度之和,即 TD (v) = ID (v) + OD (v)。

在具有 n 个顶点、e 条边的有向图中, ∑ i = 1 n I D ( v i ) = ∑ i = 1 n O D ( v i ) = e \sum_{i = 1}^{n}ID(v_i)=\sum_{i = 1}^{n}OD(v_i)=e i=1nID(vi)=i=1nOD(vi)=e

顶点 - 顶点的关系描述

  • 路径 —— 顶点 v p v_p vp到顶点 v q v_q vq之间的一条路径是指顶点序列

无向图的路径是没有方向的,也可能存在某些点之间没有路径

有向图的路径是有方向的

  • 回路 —— 第一个顶点和最后一个顶点相同的路径称为回路或环
  • 简单路径 —— 在路径序列中,顶点不重复出现的路径称为简单路径。
  • 简单回路 —— 除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路。
  • 路径长度 —— 路径上边的数目
  • 点到点的距离 —— 从顶点 u 出发到顶点 v 的最短路径若存在,则此路径的长度称为从 u 到 v 的距离。若从 u 到 v 根本不存在路径,则记该距离为无穷 ∞ \infty
  • 无向图中,若从顶点 v 到顶点 w 有路径存在,则称 v 和 w 是连通的
  • 有向图中,若从顶点 v 到顶点 w 和从顶点 w 到顶点 v 之间都有路径,则称这两个顶点是强连通的

连通图、强连通图

  • 连通图

若图 G 中任意两个顶点都是连通的,则称图 G 为连通图,否则称为非连通图。

常见考点:

对于 n 个顶点的无向图 G,
若 G 是连通图,则最少有 n-1 条边
若 G 是非连通图,则最多可能有 C n − 1 2 C_{n - 1}^2 Cn12条边

  • 强连通图

若图中任何一对顶点都是强连通的,则称此图为强连通图。

常见考点:

对于 n 个顶点的有向图 G,
若 G 是强连通图,则最少有 n 条边(形成回路)

子图

在这里插入图片描述

设有两个图 G = (V, E) 和 G’ = (V’, E’),若 V’ 是 V 的子集,且 E’ 是 E 的子集,则称 G’ 是 G 的子图

若有满足V(G’) = V(G) 的子图 G’,则称其为 G 的生成子图

PS : 并非任意挑选几个点、几条边都能构成子图

连通分量

无向图中的极大连通子图称为连通分量

  • 极大连通子图:子图必须连通,且包含尽可能多的顶点和边

有向图中的极大强连通子图称为有向图的强连通分量

  • 极大连通子图:子图必须强连通,且包含尽可能多的顶点和边

生成树、生成森林

生成树:

连通图的生成树是包含图中全部顶点的一个极小连通子图

  • 极小连通子图:边尽可能的少,但是要保持连通

若图中顶点数为n,则它生成树含有n - 1 条边。对生成树而言,若砍去它的一边,则会变成非连通图,若加上一条边则会形成一个回路。

生成森科:

在非连通图中,连通分量的生成树构成了非连通图的生成森林

边的权、带权图 / 网

  • 边的权 —— 在一个图中,每条边都可以标上具有某种含义的数值,该数值称为该边的权值。
  • 带权图 / 网 —— 边上带有权值的图称为带权图,也称网。
  • 带权路径长度 —— 当图是带权图时,一条路径上所有边的权值之和,称为该路径的带权路径长度

几种特殊形态的图

  • 无向完全图:无向图中任意两个顶点之间都存在边

若无向图的顶点数 ∣ V ∣ = n |V| = n V=n,则 ∣ E ∣ ∈ [ 0 , C n 2 ] = [ 0 , n ( n − 1 ) / 2 ] |E| \in [0, C_n^2] = [0, n(n - 1)/2] E[0,Cn2]=[0,n(n1)/2]

  • 有向完全图:有向图中任意两个顶点之间都存在方向相反的两条弧

若有向图的顶点数 ∣ V ∣ = n |V| = n V=n,则 ∣ E ∣ ∈ [ 0 , 2 C n 2 ] = [ 0 , n ( n − 1 ) ] |E| \in [0, 2C_n^2] = [0, n(n - 1)] E[0,2Cn2]=[0,n(n1)]

  • 稀疏图和稠密图是相对的

  • 树 --不存在回路,且连通的无向图

n 个顶点的树,必有 n - 1 条边。

常见考点:n 个顶点的图,若 ∣ E ∣ > n − 1 |E|>n - 1 E>n1,则一定有回路

  • 有向树–一个顶点的入度为0,其余顶点的入度均为1的有向图,称为有向树

图的存储–邻接矩阵法

图的邻接矩阵只是存储 0 和 1两种(不带权值)

实现代码:

#define MaxVertexNum 100  //顶点数目的最大值
typedef struct{
    char Vex[MaxVertexNum];  //顶点表
    int Edge[MaxVertexNum][MaxVertexNum];  //邻接矩阵,边表
    int vexnum,arcnum;  //图的当前顶点数和边数/弧数
} MGraph;

顶点中可以存储更加复杂的信息,只是因为是存储 0 和 1 所有用char

 char Vex[MaxVertexNum];  //顶点表

也可以用bool型或枚举型变量来表示边

int Edge[MaxVertexNum][MaxVertexNum];  //邻接矩阵,边表

结点数为n的图(G=(V,E))的邻接矩阵A是 n × n n \times n n×n的。将G的顶点编号为 v 1 , v 2 , … , v n v_1, v_2, \dots, v_n v1,v2,,vn,则

A [ i ] [ j ] = { 1 , 若 ( v i , v j ) 或 < v i , v j > 是 E ( G ) 中的边 0 , 若 ( v i , v j ) 或 < v i , v j > 不是 E ( G ) 中的边 A[i][j] = \begin{cases} 1, & \text{若} (v_i, v_j) \text{或} <v_i, v_j> \text{是} E(G) \text{中的边} \\ 0, & \text{若} (v_i, v_j) \text{或} <v_i, v_j> \text{不是} E(G) \text{中的边} \end{cases} A[i][j]={1,0,(vi,vj)<vi,vj>E(G)中的边(vi,vj)<vi,vj>不是E(G)中的边

在无向图中:

第 i 个结点的度 = 第 i 行(或第 i 列)的非零元素的个数

时间复杂度:O(|v|)

在有向图中:

第 i 个结点的出度 = 第 i 行的非零元素个数

第 i 个结点的入度 = 第 i 列的非零元素个数

第 i 个结点的度 = 第 i 行、第 i 列的非零元素个数之和

时间复杂度:O(|v|)

图的邻接矩阵(带权值)/ 网

如果两点之间不村子边的话,我们用无穷(∞)来表示它们之间不存在相连的边

实现代码:

#define MaxVertexNum 100    //顶点数目的最大值
#define INFINITY 最大的int值   //宏定义常量"无穷"
typedef char VertexType;    //顶点的数据类型
typedef int EdgeType;       //带权图中边上权值的数据类型
typedef struct{
    VertexType Vex[MaxVertexNum];       //顶点
    EdgeType Edge[MaxVertexNum][MaxVertexNum]; //边的权
    int vexnum,arcnum;          //图的当前顶点数和弧数
}MGraph;

可用int 的上限值表示“无穷”

#define INFINITY 最大的int值   //宏定义常量"无穷"

邻接矩阵的性能分析

空间复杂度: O ( ∣ V ∣ 2 ) O(|V|^2) O(V2) —— 只和顶点数相关,和实际的边数无关

适合用于存储稠密图

无向图的邻接矩阵是对称矩阵,可以压缩存储(只存储上三角区 / 下三角区)

邻接矩阵的性质

在这里插入图片描述

ABCD
A0100
B1011
C0101
D0110

设图G的邻接矩阵为A(矩阵元素为(0/1)),则 A n A^n An的元素 A n [ i ] [ j ] A^n[i][j] An[i][j]等于由顶点i到顶点j的长度为n的路径的数目

在这里插入图片描述

A 2 [ 1 ] [ 4 ] = a 1 , 1 a 1 , 4 + a 1 , 2 a 2 , 4 + a 1 , 3 a 3 , 4 + a 1 , 4 a 4 , 4 = 1 A^2[1][4] = a_{1,1}a_{1,4} + a_{1,2}a_{2,4} + a_{1,3}a_{3,4} + a_{1,4}a_{4,4} = 1 A2[1][4]=a1,1a1,4+a1,2a2,4+a1,3a3,4+a1,4a4,4=1

A 2 [ 2 ] [ 2 ] = a 2 , 1 a 1 , 2 + a 2 , 2 a 2 , 2 + a 2 , 3 a 3 , 2 + a 2 , 4 a 4 , 2 = 3 A^2[2][2] = a_{2,1}a_{1,2} + a_{2,2}a_{2,2} + a_{2,3}a_{3,2} + a_{2,4}a_{4,2} = 3 A2[2][2]=a2,1a1,2+a2,2a2,2+a2,3a3,2+a2,4a4,2=3

A 2 [ 3 ] [ 3 ] = a 3 , 1 a 1 , 3 + a 3 , 2 a 2 , 3 + a 3 , 3 a 3 , 3 + a 3 , 4 a 4 , 3 = 1 A^2[3][3] = a_{3,1}a_{1,3} + a_{3,2}a_{2,3} + a_{3,3}a_{3,3} + a_{3,4}a_{4,3} = 1 A2[3][3]=a3,1a1,3+a3,2a2,3+a3,3a3,3+a3,4a4,3=1

A 2 [ 1 ] [ 2 ] = a 1 , 1 a 1 , 2 + a 1 , 2 a 2 , 2 + a 1 , 3 a 3 , 2 + a 1 , 4 a 4 , 2 = 0 A^2[1][2] = a_{1,1}a_{1,2} + a_{1,2}a_{2,2} + a_{1,3}a_{3,2} + a_{1,4}a_{4,2} = 0 A2[1][2]=a1,1a1,2+a1,2a2,2+a1,3a3,2+a1,4a4,2=0

在这里插入图片描述

图的存储–邻接表(顺序 + 链式存储)

邻接表适用于存储稀疏图

实现代码:

// 图结构体(邻接表存储)
typedef struct{
    AdjList vertices;         // 顶点数组
    int vexnum;               // 图的当前顶点数
    int arcnum;               // 图的当前边/弧数
}ALGraph;

// 顶点结点结构体
typedef struct VNode{
    VertexType data;          // 顶点存储的数据
    ArcNode *first;           // 指向第一条依附于该顶点的边/弧
}VNode, AdjList[MaxVertexNum];  // AdjList为顶点数组类型

// 边/弧结点结构体
typedef struct ArcNode{
    int adjvex;               // 边/弧指向的顶点下标
    struct ArcNode *next;     // 指向下一条边/弧的指针
    // InfoType info;          // 边/弧的权值等附加信息(可根据需要启用)
}ArcNode;

这个方法与孩子表示法相似

无向图:

边结点的数量是 2 ∣ E ∣ 2|E| 2∣E,整体空间复杂度为$ O(|V| + 2|E|)$

有向图:

边结点的数量是$ |E| ,整体空间复杂度为 ,整体空间复杂度为 ,整体空间复杂度为 O(|V| + |E|)$

图的邻接表的表示方式不唯一

图的邻接矩阵:只要确定了顶点编号,图的邻接矩阵表示方法唯一

图的存储–十字链表(存储有向图)

在这里插入图片描述

空间复杂度:$ O(|V| + |E|)$

如何找到指定顶点的所有出边?

–顺着绿色线路找

如何找到指定顶点的所有入边?

–顺着橙色线路找

图的存储–邻接多重表(存储无向图)

在这里插入图片描述

空间复杂度:$ O(|V| + |E|)$

删除边、删除节点等操作很方便

图的基本操作

图的存储常考考点:邻接矩阵 、 邻接表

<>这个是有向图,()这个是无向图

  • Adjacent(G,x,y):判断图G是否存在边<x, y>(x, y)

无向图 :邻接矩阵时间复杂度:O(1) 、 邻接表时间复杂度:O(1)

有向图 :邻接矩阵时间复杂度:O(1) 、 邻接表时间复杂度:O(1) ~ O(|v|)

  • Neighbors(G,x):列出图G中与结点x邻接的边。

无向图 :O(|v|) 、 邻接表时间复杂度:O(1) ~ O(|v|)

有向图 :邻接矩阵时间复杂度:O(|v|) 、 邻接表时间复杂度:出边 :O(1) ~ O(|v|),入边 :$ O(|E|)$

  • InsertVertex(G,x):在图G中插入顶点x

无向图 :邻接矩阵时间复杂度O(1) 、 邻接表时间复杂度:O(1)

有向图 :邻接矩阵时间复杂度O(1) 、 邻接表时间复杂度:O(1)

  • DeleteVertex(G,x):从图G中删除顶点x

无向图 :邻接矩阵时间复杂度:O(|v|) 、 邻接表时间复杂度:O(1) ~ $ O(|E|)$

有向图 :邻接矩阵时间复杂度:O(|v|) 、 邻接表时间复杂度:删出边 :O(1) ~ O(|v|),删入边 :$ O(|E|)$

  • AddEdge(G,x,y):若无向边(x, y)或有向边<x, y>不存在,则向图G中添加该边。

无向图 :邻接矩阵时间复杂度O(1) 、 邻接表时间复杂度:O(1)

有向图 :邻接矩阵时间复杂度O(1) 、 邻接表时间复杂度:O(1)

  • RemoveEdge(G,x,y):若无向边(x, y)或有向边<x, y>存在,则从图G中删除该边。

无向图 :邻接矩阵时间复杂度O(1) 、 邻接表时间复杂度:O(1) ~ $ O(|E|)$

有向图 :邻接矩阵时间复杂度O(1) 、 邻接表时间复杂度:O(1) ~ $ O(|E|)$

  • FirstNeighbor(G,x):求图G中顶点x的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-1

无向图 :邻接矩阵时间复杂度O(1) ~ O(|v|) 、 邻接表时间复杂度:O(1)

有向图 :邻接矩阵时间复杂度O(1) ~ O(|v|)、 邻接表时间复杂度:找出边邻接点:O(1),找入边邻接点O(1) ~ $ O(|E|)$,但是一般只考察找出边的操作

  • NextNeighbor(G,x,y):假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若yx的最后一个邻接点,则返回-1

无向图 :邻接矩阵时间复杂度O(1) ~ O(|v|) 、 邻接表时间复杂度:O(1)

  • Get_edge_value(G,x,y):获取图G中边(x, y)<x, y>对应的权值。

无向图 :邻接矩阵时间复杂度O(1) 、 邻接表时间复杂度:O(1) ~ O(|v|)

  • Set_edge_value(G,x,y,v):设置图G中边(x, y)<x, y>对应的权值为v

常考的:FirstNeighbor(G,x)NextNeighbor(G,x,y)

图的广度优先遍历(BFS)

树 VS 图

树 :不存在 “ 回路 ” ,搜索相邻的结点时,不可能搜到已经访问过的结点

图:搜索相邻的顶点时,有可能搜到已经访问过的顶点

​ 解决方法(设置一个标记,用来标记已经搜索到的顶点)

广度优先遍历(Breadth - First - Search, BFS)要点

  1. 找到与一个顶点相邻的所有顶点
  2. 标记哪些顶点被访问过
  3. 需要一个辅助队列

相关函数

  • FirstNeighbor(G, x):求图 G 中顶点 x 的第一个邻接点,若有则返回顶点号;若 x 没有邻接点或图中不存在 x,则返回 - 1。
  • NextNeighbor(G, x, y):假设图 G 中顶点 y 是顶点 x 的一个邻接点,返回除 y 之外顶点 x 的下一个邻接 点的顶点号,若 y 是 x 的最后一个邻接点,则返回 - 1。

广度优先遍历序列:

在这里插入图片描述

从顶点1出发的广度优先遍历序列:

1,2,5,6,3,7,4,8

从顶点3出发的广度优先遍历序列:

3,4,6,7,8,2,1,5

同一个图的邻接矩阵表示方式唯一,因此广度优先遍历序列唯一

同一个图的邻接表表示方式不唯一,因此广度优先遍历序列不唯一

实现代码:

bool visited[MAX_VERTEX_NUM];  //访问标记数组,初始都为 false
void BFSTraverse(Graph G){  //对图G进行广度优先遍历
    for(i=0;i<G.vexnum;++i)
        visited[i]=FALSE;   //访问标记数组初始化
    InitQueue(Q);            //初始化辅助队列Q
    for(i=0;i<G.vexnum;++i)  //从0号顶点开始遍历
        if(!visited[i])      //对每个连通分量调用一次BFS
            BFS(G,i);        //vi未访问过,从vi开始BFS
}
//广度优先遍历
void BFS(Graph G,int v){   //从顶点v出发,广度优先遍历图G
    visit(v);               //访问初始顶点v
    visited[v]=TRUE;        //对v做已访问标记
    Enqueue(Q,v);           //顶点v入队列Q
    while(!isEmpty(Q)){
        DeQueue(Q,v);       //顶点v出队列
        for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
            //检测v所有邻接点
            if(!visited[w]){   //w为v的尚未访问的邻接顶点
                visit(w);      //访问顶点w
                visited[w]=TRUE;//对w做已访问标记
                EnQueue(Q,w);  //顶点w入队列
            }//if
    }//while
}
  • 对于无向图来说,调用BFS函数的次数 = 连通分量数

空间复杂度:最坏的情况,辅助队列大小为 O(|v|)

时间复杂度:

邻接矩阵:

访问 |V| 个顶点需要 O (|V|) 的时间

查找每个顶点的邻接点都需要 O (|V|) 的时间,而总共有 |V| 个顶点

时间复杂度 = O (|V|²)

邻接表

访问 |V| 个顶点需要 O (|V|) 的时间

查找各个顶点的邻接点共需要 O (|E|) 的时间

时间复杂度 = O (|V| + |E|)

  • 广度优先生成树

广度优先生成树由广度优先遍历过程确定。由于邻接表的表示方式不唯一,因此基于邻接表的广度优先生成树也不唯一。

  • 广度优先生成森林

对非连通图的广度优先遍历,可得到广度优先生成森林

图的深度优先遍历(DFS)

图的深度优先遍历与树的先根遍历相类似

树的先根遍历:

//树的先根遍历
void PreOrder(TreeNode *R){
    if (R!=NULL){
        visit(R);  //访问根节点
        while(R还有下一个子树T)
            PreOrder(T);  //先根遍历下一棵子树
    }
}

图的深度优先遍历:

在这里插入图片描述

从2出发的深度优先遍历序列:

2,1,5,6,3,4,7,8

实现代码:

bool visited[MAX_VERTEX_NUM];  //访问标记数组
void DFSTraverse(Graph G){     //对图G进行深度优先遍历
    for(v=0;v<G.vexnum;++v)    //初始化已访问标记数据
        visited[v]=FALSE;  
    for(v=0;v<G.vexnum;++v)    //本代码中是从v=0开始遍历
        if(!visited[v])        
            DFS(G,v);          
}

void DFS(Graph G,int v){       //从顶点v出发,深度优先遍历图G
    visit(v);                  //访问顶点v
    visited[v]=TRUE;           //设已访问标记
    for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
        if(!visited[w]){       //w为u的尚未访问的邻接顶点
            DFS(G,w);          
        } //if
}

空间复杂度:来自函数调用栈,最坏情况,递归深度为 O (|V|) -->(无特殊情况下是这个复杂化度)

空间复杂度:最好情况,O (1)

时间复杂度:访问各个结点所需要的时间 + 探索各条边所需时间

邻接矩阵:

访问 |V| 个顶点需要 O (|V|) 的时间

查找每个顶点的邻接点都需要 O (|V|) 的时间,而总共有 |V| 个顶点

时间复杂度 = O (|V|²)

邻接表:

访问 |V| 个顶点需要 O (|V|) 的时间

查找各个顶点的邻接点共需要 O (|E|) 的时间

时间复杂度 = O (|V| + |E|)

  • 深度优先生成树:

同一个图的邻接矩阵表示方式唯一,因此深度优先遍历序列唯一,深度优先生成树也唯一

同一个图的邻接表表示方式不唯一,因此深度优先遍历序列不唯一 ,深度优先生成树也不唯一

图的遍历与图的连通性:

无向图进行 BFS/DFS 遍历 调用 BFS/DFS 函数的次数 = 连通分量数

对于连通图,只需调用 1 次 BFS/DFS

有向图进行 BFS/DFS 遍历 调用 BFS/DFS 函数的次数要具体问题具体分析

若起始顶点到其他各顶点都有路径,则只需调用 1 次 BFS/DFS 函数

对于强连通图,从任一结点出发都只需要调用 1 次 BFS/DFS 函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

YA10JUN

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

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

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

打赏作者

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

抵扣说明:

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

余额充值