十、图(基本知识,存储结构,遍历方法)

1、图的定义和基本术语

\qquad G = ( V , E ) G=(V,E) G=(V,E) V V V顶点(数据元素)的有穷非空集合; E E E边的有穷集合。
\qquad 无向图:每条边都是无方向的;有向图:每条边都是有方向的。
\qquad 完全图:图中任意两个顶点都有一条边相连。
在这里插入图片描述 \qquad 对于有n个顶点的无向完全图,要有 n ( n − 1 ) / 2 n(n-1)/2 n(n1)/2条边;对于有n个顶点的有向完全图,要有 n ( n − 1 ) n(n-1) n(n1)条边。
\qquad 稀疏图:有很少边或者弧的图 e < n l o g n e<nlogn e<nlogn
\qquad 稠密图:有较多边或者弧的图。
\qquad 网:边/弧带权重的图。
\qquad 邻接:有边/弧相连的两个顶点之间的关系,存在 ( v i , v j ) (v_i, v_j) (vi,vj),则称 v i v_i vi v j v_j vj互为邻接点;存在 < v i , v j > <v_i,v_j> <vi,vj>,则称 v i v_i vi邻接到 v j v_j vj v j v_j vj邻接于 v i v_i vi
\qquad 关联(依附):边/弧与顶点之间的关系,存在 ( v i , v j ) , < v i , v j > (v_i, v_j), <v_i,v_j> (vi,vj),<vi,vj>,则称该边/弧关联于 v i v_i vi v j v_j vj
\qquad 顶点的度:与该顶点相关联的边的数量,即为 T D ( v ) TD(v) TD(v),在有向图中,顶点的度等于该顶点的入度和出度之和。顶点的入度是以v为终点的有向边的条数,记作 I D ( v ) ID(v) ID(v);顶点的出度为以v为出发点的有向边的条数,记作 O D ( v ) OD(v) OD(v)
在这里插入图片描述
\qquad 路径:接续的便构成的顶点序列;
\qquad 路径长度:路径上边或者弧的数目/权值之和;
\qquad 回路(环):第一个顶点和最后一个顶点相同的路径;
\qquad 简单路径:除路径的起点和终点可以相同外,其余顶点均不相同的路径;
\qquad 简单环:除路径的起点和终点相同外,其余顶点均不相同的路径;
在这里插入图片描述
\qquad 连通图(强连通图):在无(有)向图 G = ( V , E ) G=(V,E) G=(V,E)中,若对于任意两个顶点 v , u v,u v,u,都存在从 v v v u u u的路径,则称图 G G G是连通图(强连通图)。
在这里插入图片描述
\qquad 权和网:图中边或者弧具有相关数称为权。表明从一个顶点到另一个顶点的距离或者耗费。带权的图称为网。
\qquad 子图:设有两个图 G = ( V , E ) , G 1 = ( V 1 , E 1 ) G=(V,E), G_1=(V_1,E_1) G=(V,E),G1=(V1,E1),若满足 V 1 ∈ V , E 1 ∈ E V_1 \in V, E_1 \in E V1V,E1E,则称 G 1 G_1 G1 G G G的子图。
在这里插入图片描述
\qquad 连通分量(强连通分量):无向图 G G G的极大连通子图称为 G G G的连通分量,极大连通的意思是:该子图是 G G G的连通子图,将 G G G中任意一个不再该子图中的顶点加入之后,该子图不再连通。
在这里插入图片描述
\qquad 有向图 G G G的极大强连通子图称为 G G G的强连通分量。极大强连通子图的意思是:该子图是 G G G的强连通子图,将 D D D的任何不在该子图中的顶点加入,子图不再是强连通的。
在这里插入图片描述
\qquad 极小连通子图:该子图是 G G G的连通子图,在该子图中删除任意一条边,子图不再连通。
\qquad 生成树:包含无向图 G G G的所有顶点的极小连通子图;
\qquad 生成森林:对于非连通图,由各个连通分量的生成树的集合。
在这里插入图片描述

1.1 图的数据类型定义

\qquad 图的抽象类型定义如下所示:
在这里插入图片描述
\qquad 图的基本操作包括以下几种:
在这里插入图片描述

2、图的存储结构

\qquad 图是多对多的逻辑结构,图没有顺序存储结构,但可以借助二维数组来表示元素之间的关系,如邻接矩阵,邻接表,邻接多重表,十字链表等。

2.1 邻接矩阵

\qquad 首先建立一个顶点表(记录各个顶点的信息),和一个邻接矩阵(表示各个顶点之间的关系)。设图 A = ( V , E ) A=(V,E) A=(V,E)有n个顶点,则顶点表的表示如下所示:
在这里插入图片描述
\qquad 图的邻接矩阵是一个二维数组 A . a r c s [ n ] [ n ] A.arcs[n][n] A.arcs[n][n],定义为:
在这里插入图片描述
\qquad 下图是一个无向图邻接矩阵的示意图:
在这里插入图片描述
\qquad 可以发现无向图的邻接矩阵是一个对称矩阵,同时对角线的元素均为0;顶点 i i i的度等于第 i i i行(列)中1的个数;完全图的邻接矩阵中,对角线元素为0,其余元素均为1。
\qquad 下图是一个有向图邻接矩阵的示意图:
在这里插入图片描述
\qquad 在有向图邻接矩阵中,第 i i i行的含义是:以结点 v i v_i vi为尾的弧,即出度边;第 i i i列的含义是:以结点 v i v_i vi为头的弧,即入度边。有向图的邻接矩阵可能是不对称的;顶点的出度=第 i i i行的元素之和;顶点的入度=第 i i i列的元素之和;有向图顶点的度=第 i i i行的元素之和+第 i i i列的元素之和。
\qquad 网-即有权图的邻接矩阵表示法,定义如下所示:
在这里插入图片描述 \qquad 下图是一个网的邻接矩阵的示意图:
在这里插入图片描述
\qquad 邻接矩阵的存储表示:用两个数组分别存储顶点表邻接矩阵

#define MaxInt 50000			//定义一个极大的数
#define MVNum 100				//定义最大的顶点数	
typedef char VerTexType;		//设顶点的数据类型为字符型
typedef int ArcType;			//设边的权值类型为整数型

typedef struct
{
	VerTexType vexs[MVNum];		//顶点表
	ArcType arcs[MVNum][MVNum];	//邻接矩阵
	int vexNum, arcNum;			//图当前的节点数量和边数量
}AMGraph;

\qquad 采用邻接矩阵构建无向网:

//创建图
void CreatUDN(AMGraph &G)
{
	cin >> G.vexNum >> G.arcNum;
	for(int i = 0; i < G.verNum; ++i)
		cin>> G.vex[i];		//依次输入点的信息
	for(int i = 0; i < G.vexNum; ++i)	//初始化邻接矩阵
		for(int j = 0; j < G.vexNum; ++j)
			G.arcs[i][j] = MaxInt;
	//给边赋值
	for(int k = 0; k < G.arcNum; ++k)
	{
		int v1 = 0, v2 = 0;
		double w = 0;
		cin >> v1 >> v2 >> w;		//输入当前边的顶点和边的权重
		int i = LocateVex(G, v1);	//确定出点v1和v2在图中的位置
		int j = LocateVex(G, v2);
		G.arcs[j][i] = G.arcs[i][j] = w;//无向图是对称的
	}
}

//在图中查找顶点
int LocateVex(AMGraph &G, VertexType u)
{
	for(int i = 0; i < G.verNum; ++i)
		if(u==G.vers[i]) return i;
	return -1;	
}

2.2 邻接矩阵的优缺点

\qquad 优点: 直观,简单,好理解;方便检查任意一对顶点间是否存在边;方便找任意一个顶点的所有“邻接点”,有边直接相连的顶点;方便计算任意一个顶点的“度”,从该点发出的边的数量为“出度”;指向该点的边的数量为“入度”。
\qquad 缺点: 不便于增加和删除顶点;浪费空间, O ( n 2 ) , n O(n^2),n O(n2),n是顶点的数量,如果存稀疏图(点很多,边很少),则会有大量无效元素;浪费时间,统计稀疏图中有多少条边。

2.3 邻接表

\qquad 邻接表表示法使用链式存储的结构,顶点按照编号顺序将顶点数据存储在一维数组之中;关联同一顶点的边,用线性链表进行存储。邻接表的示意图如下所示:
在这里插入图片描述
\qquad 邻接表是不唯一的,因为链表中的边的顺序可以互换;若无向图中有n个顶点,e条边,则其邻接表需要n个头结点和2e个表结点。适合存储稀疏图。所以邻接表的空间复杂度为: O ( n + 2 e ) O(n+2e) O(n+2e);无向图中顶点 v i v_i vi的度为第 i i i个单链表中的结点个数。
\qquad 有向图的邻接表如下图所示:
在这里插入图片描述
\qquad 有向图邻接表的空间复杂度为 O ( n + e ) O(n+e) O(n+e);容易计算顶点 v i v_i vi的出度即为第 i i i个单链表中结点的个数;顶点 v i v_i vi的入度即为整个单链表中邻接点域值为 i − 1 i-1 i1的结点的个数,计算比较复杂。
\qquad 上图是以顶点的出度建立的出度边邻接表,还可以使用顶点的入度边建立入度边邻接表,如下图所示:
在这里插入图片描述
\qquad 容易计算顶点 v i v_i vi的入度即为第 i i i个单链表中结点的个数;顶点 v i v_i vi的出即为整个单链表中邻接点域值为 i − 1 i-1 i1的结点的个数,计算比较复杂。
\qquad 当已知一个邻接矩阵或者邻接表,可以唯一确定一个无向图/有向图。
\qquad 邻接表的存储表示:

//顶点的结构
#define MVnum 100	//最大顶点数量
typedef struct VNode
{
	VerTexType data;	//顶点信息
	ArcNode *firstArc;	//指向第一条依附于该顶点的边的指针
}AdjList[MVnum];		//AdjList表示邻接表类型
//边的节点结构
typedef struct ArcNode
{
	int adjVex;		//该边指向的顶点的位置
	ArcNode *nextArc;	//指向下一条边的指针
	double info;		//边的权值
};
//图的结构定义
typedef struct ALGraph
{
	AdjList vertices;	//邻接表中的顶点表
	int vexNum, arcNum;//图中的顶点数和边数
};
//邻接表的构建
void CreateUDG(ALGraph &G)
{
	cin >> G.vexNum >> G.arcNum;	//输入总的顶点数和总的边数
	for(int i = 0; i < G.vexNum; ++i)
	{//构建顶点表
		cin >> G.vertices[i].data;
		G.vertices[i].firstArc = nullptr;	//初始化头结点的指针域为空
	}
	for(k= 0; k <G.arcNum; ++k)
	{//构建边链表
		cin >> v1 >> v2;		//输入一条边依附的两个顶点
		int i = LocateVex(G,v1);
		int j = LocateVex(G,v2);
		ArcNode p1 = new ArcNode;//生成一个新的边结点
		p1->adjVex = j;	//邻接点的序号为j
		p1->nextArc = G.vertices[i].firstArc;//头插法
		G.vertices[i].firstArc = p1;	//将新结点插入到顶点vi的头部
		//构建无向网
		ArcNode p2 = new ArcNode;//生成一个新的边结点
		p2->adjVex = i;	
		p2->nextArc = G.vertices[j].firstArc;//头插法
		G.vertices[j].firstArc = p2;	
	}
}

2.4 邻接表的优缺点

\qquad 方便找到任一顶点的所有“邻接点”;
\qquad 节约稀疏图的空间,需要N个头指针+2E个结点;
\qquad 不方便计算某个顶点的度:对于无向图可以直接通过和头结点相连的边结点的个数获得结点的度;
\qquad 但是对于有向图,只能计算节点的出度,需要构造逆邻接表来方便计算结点的入度;
\qquad 不方便检查任意一对顶点之间是否存在边;
\qquad 邻接矩阵常用于稠密图,邻接表常用于稀疏图

2.5 十字链表和邻接多重表

\qquad 使用十字链表来存储有向图可以克服有向图求结点的度困难的问题;使用邻接多重表存储无向图可以解决无向图每条边都要存储两边的问题。
\qquad 十字链表可以看做是有向图的邻接表和逆邻接表结合起来形成的一种链表。在原有向图邻接表的基础之上,给头结点增加一个指针域指向节点的第一条出度边;给边结点的增加一个指针域指向弧头相同的节点,之前边结点的指针域指向的是弧尾相同的节点;增加一个节点值域,记录边的尾节点的值。
在这里插入图片描述
\qquad 邻接多重表中每一条边只申请一个边结点,增加几个指针域将边结点和顶点之间的关系表示出来即可。
在这里插入图片描述

3、图的遍历

\qquad 遍历的定义: 从已给的连通图中某个顶点出发,沿着一些边访问遍图中所有的顶点,且使得每个顶点仅被访问一次,就叫做图的遍历,它是图的基本运算。图遍历的实质是:找每个顶点的邻接点的过程。
\qquad 图的特点: 图中可能存在回路,且图的任一顶点都可能与其他顶点相连通,在访问完某个顶点之后,可能会沿着某些边右回到曾经访问过的顶点。可以设置一个辅助数组visited[n],记录已经被访问过的顶点,初始状态均置为0,若顶点i被访问过了,则visited[i]置为1,从而放置顶点的多次访问。

3.1 深度优先搜索-DFS

\qquad 在访问图中的某个起始顶点v之后,由v出发,访问它的任意一个邻接顶点 w 1 w_1 w1
\qquad 再从 w 1 w_1 w1出发,访问与 w 1 w_1 w1邻接但是还没有被访问过的顶点 w 2 w_2 w2
\qquad 然后再从 w 2 w_2 w2出发,进行类似的访问… ;
\qquad 如此进行下去,直到到达所有的邻接顶点都被访问过的u为止;
\qquad 接着退回一步,退到前一次刚访问过的顶点,看是否还有其他没有被访问的邻接顶点;
\qquad 如果有,则访问此顶点,之后再从此顶点出发,进行与之前类似的访问;
\qquad 如果没有,则再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止。
\qquad 连通图的深度优先遍历类似于树的先根遍历。
\qquad 采用邻接矩阵表示图的深度优先搜索遍历的实现如下所示:

void DFS(AMGraph &G, int v)
{
	cout << v << endl;
	visited[v] = true;	//visited为辅助记录节点是否访问的向量
	for(int i = 0; i < G.vexNum; ++i)
	{
		if(G.arcs[v][i]!=0 && !visited[i])
			DFS(G, i);
	}
}

\qquad 用邻接矩阵来表示图,遍历图中每个点点都要从头扫描该顶点所在的行,时间复杂度为 O ( n 2 ) O(n^2) O(n2)
\qquad 用邻接表来表示图,虽然有2e个表结点,但只需要扫描e个结点即可以完成遍历,加上访问n个头结点的时间,时间复杂度为 O ( n + e ) O(n+e) O(n+e)
\qquad 所以稠密图适合在邻接矩阵上进行深度遍历;稀疏图适合在邻接表上进行深度遍历。
\qquad 非连通图的遍历:首先从图中任意选择一个节点进行DFS,当DFS结束之后,若visited还有没有访问的节点,则再从图中任意选择一个节点进行DFS;重复上述过程,直到visited中所有的节点均被访问过。

3.2 广度优先搜索-BFS

\qquad 从图的某个结点出发,首先依次访问该结点的所有邻接顶点 v 1 , v 2 , . . . , v m v_1,v_2,...,v_m v1,v2,...,vm
\qquad 再按照这些顶点被访问的先后顺序依次访问与它们相邻接的所有未被访问的顶点;
\qquad 重复上述过程,直到所有顶点均被访问了
\qquad 借助队列实现图的广度优先遍历:

void BFS(Graph &G, int v)
{
	cout << v;
	visited[v]=true;
	queue<int> Q;
	Q.push(v);
	while(!Q.empty())
	{
		int top = Q.top();
		Q.pop();
		for(int w = FistAdjVex(G,top); w>=0; w=NextAdjVex(G,top,w))
		{
			if(!visited[w])
			{
				cout << w<<endl;
				visited[w] = true;
				Q.push(w);
			}
		}
	}
}

\qquad 如果使用邻接矩阵,则对于BFS每一个被访问到的顶点,都要循环检测矩阵中的一行n个元素,总的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。和深度优先遍历相同,使用邻接表的时间复杂度为 O ( n + e ) O(n+e) O(n+e)
\qquad DFS和BFS的空间复杂度相同,都是 O ( n ) O(n) O(n),DFS借助了栈(递归借助系统中的栈),BFS借助了队列。

4、图的应用

4.1 生成树

\qquad 生成树: 所有顶点均由边连接在一起,但不存在回路的图。
\qquad 一个图中可以有许多棵不同的生成树;
\qquad 所有生成树具有以下共同特点:
\qquad\qquad · 生成树的顶点个数与图的顶点个数相同;
\qquad\qquad · 生成树是图的极小连通子图,去掉一条边则非连通;
\qquad\qquad · 一个有n个顶点的连通图的生成树有n-1条边;
\qquad\qquad · 在生成树中再加一条边必然形成回路。
\qquad\qquad · 生成树中任意两个顶点之间的路径唯一
\qquad 含有n个顶点n-1条边的图不一定是生成树
\qquad 设图 G = ( V , E ) G=(V,E) G=(V,E)是个连通图,当从图中任意一个顶点出发遍历图G时,将边集合 E ( G ) E(G) E(G)分成两个集合 T ( G ) T(G) T(G) B ( G ) B(G) B(G)。其中, T ( G ) T(G) T(G)是遍历图时经过的边的集合, B ( G ) B(G) B(G)是遍历图时未经过的边的集合。显然, G 1 ( V , T ) G1(V,T) G1(V,T)是图G的极小连通子图,即子图 G 1 G1 G1是图G的生成树。
\qquad 最小生成树:给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那棵生成树称为该网的最小生成树,也叫最小代价生成树。
\qquad MST性质:设 N = ( V , E ) N=(V,E) N=(V,E)是一个连通网, U U U是顶点集 V V V的一个非空子集。若边 ( U , V ) (U,V) (U,V)是一条具有最小权值的边,其中 u ∈ U , v ∈ V − U u\in U, v \in V-U uU,vVU,则必然存在一棵包含边 ( u , v ) (u,v) (u,v)的最小生成树。
\qquad Prim算法: 算法思想:设 N = ( V , E ) N=(V,E) N=(V,E)是连通网, T E TE TE N N N上最小生成树中的边的集合;初始令 U = u 0 , T E = { } U={u_0},TE=\{\} U=u0,TE={};在所有 u ∈ U , v ∈ V − U u \in U,v \in V-U uU,vVU的边 ( u , v ) ∈ E (u,v) \in E (u,v)E中,找一条代价最小的边 ( u 0 , v 0 ) (u_0,v_0) (u0,v0);将 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)并入边集合 T E TE TE,同时将 v 0 v_0 v0并入 U U U;重复上述操作,直至 U = V U=V U=V为止,则 T = ( V , T E ) T=(V,TE) T=(V,TE) N N N的最小生成树。
\qquad Kruskal算法: 算法思想:设连通网 N = ( V , E ) N=(V,E) N=(V,E),令最小生成树初始状态是只有n个顶点但是没有边的非连通图 T = ( V , ) T=(V,{}) T=(V,),每个顶点自成一个连通分量。在E中选取代价最小的边,若改变依附的顶点落在 T T T中不同的连通分量上(即不能形成环),则将此边加入到 T T T中;否则去掉此边,选取下一条代价最小的边;依次类推,直至T中所有顶点都在同一个连通分量上为止。
\qquad Prim算法 的时间复杂度为: O ( n 2 ) O(n^2) O(n2),其中n为顶点数量,所以适用于稠密图;Kruskal算法的时间复杂度为: O ( e l o g e ) O(eloge) O(eloge),其中e为边的数量,所以适用于稀疏图。

4.2 最短路径

\qquad 交通网络使用有向网来表示:顶点表示地点;弧表示两个地点之间有路连通;弧上的权值表示两个地点之间的距离,交通费或者图中所花费的时间等。如何使得从一个地点到另外一个地点的运输时间最短或者运费最省,这就是一个求两个地点之间的最短路径问题。
\qquad 问题抽象:在有向网中从源点到终点的多条路径中,寻找一条各边的权值之和最小的路径,即最短路径,最短路径与最小生成树不同,路径上不一定包含n个顶点,也不一定包含n-1条边。

4.2.1 单源最短路径问题-Dijkstra算法

\qquad 求图中某个顶点到另外其余顶点的最短路径,叫做单源最短路径问题,常用的算法时Dijkstra算法。Dijkstra算法的流程如下所示:
\qquad 1)初始化:先找出从源点 v 0 v_0 v0到各终点 v k v_k vk的直达路径 ( v 0 , v k ) (v_0,v_k) (v0,vk),即通过一条弧到达的路径;
\qquad 2)选择:从这些路径中找出一条长度最短的路径 ( v 0 , v u ) (v_0,v_u) (v0,vu)
\qquad 3)更新:然后对其余各条路径进行适当地调整:若在图中存在弧 ( u , v k ) (u,v_k) (u,vk),且 ( v 0 , u ) + ( u , v k ) < ( v 0 , v k ) (v_0,u)+(u,v_k)<(v_0,v_k) (v0,u)+(u,vk)<(v0,vk),则以路径 ( v 0 , u , v k ) (v_0,u,v_k) (v0,u,vk)代替 ( v 0 , v k ) (v_0,v_k) (v0,vk)
\qquad 4)在调整之后的各条路径中,再找长度最短的路径,以此类推。
\qquad Dijkstra算法的示意图如下图所示:
在这里插入图片描述

4.2.2 多源最短路径问题-Floyd算法

\qquad 上述介绍的Dijkstra算法可以计算某个点到其他点的最短路,但是如果想要计算每个点到其他的最短路需要将Dijkstra算法循环顶点个数次。
\qquad 另外一种求多源最短路径问题的算法时Floyd算法,Floyd算法的思想如下所示:首先进行逐个顶点的额试探;之后从 v i v_i vi v j v_j vj的所有可能存在的路径中,选出一条长度最短的路径。Floyd算法的思路演示如下图所示:
在这里插入图片描述

4.3 拓扑排序

\qquad 拓扑排序和关键路径这两种应用针对的是一种特殊的图-有向无环图,简称DAG图(Directed Acycline Graph)。DAG的示意图如下图所示:
在这里插入图片描述
\qquad AOV网,用一个有向图表示一个工程的各个子工程机器相互制约的关系,其中以顶点表示活动,弧表示活动之间的优先制约关系,称这种有向图为顶点表示活动的网,简称AOV网-Activity On Vertex network。应用AOV网解决拓扑排序问题。
\qquad AOE网,用一个有向图表示一个工程的各个子工程机器相互制约的关系,其中以弧表示活动,顶点表示活动的开始或者结束事件,称这种有向图为边表示活动的网,简称AOE网-Activity On Edge network。应用AOE网解决关键路径问题。
\qquad AOV网的特点:若从i到j有一条有向路径,则i是j的前驱,j是i的后继;若<i,j>是网中的有向边,则i是j的直接前驱,j是i的直接后继。AOV网中不允许有回路存在,因为如果有回路存在,则表明某项活动以自己为先决条件,显然这是荒谬的。
\qquad 在AOV网没有回路的前提下,我们将全部活动排列成一个线性序列,使得若AOV网中有弧<i,j>存在,则在这个序列中,i一定排在j的前面,具有这种性质的线性序列称为拓扑有序序列,相应的拓扑有序排序的算法称为拓扑排序
\qquad 拓扑排序的方法:在有向图中选一个没有前驱的顶点并且进行输出;从图中删除该顶点和所有以它为尾的弧;重复上述两步,直至全部顶点均已输出或者当图中不存在无前驱的顶点为止。拓扑排序的示意如下图所示:
在这里插入图片描述
\qquad 检测AOV网中是否存在环的方法:对有向图构造其顶点的拓扑有序序列,若网中所有顶点都在它的拓扑有序序列中,则该AOV网必定不存在环。

4.4 关键路径

\qquad 把工程计划表示为边,以此来表示活动的网络,即AOV网。用顶点表示时间,弧表示活动,弧的权值表示活动的持续时间。事件表示在它之前的活动已经完成,在它之后的活动可以开始。
\qquad 关键路径的求解方法可以参考运筹学课程。

THE END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Dragon Fly

多谢老板赏钱[抱拳抱拳抱拳]

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

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

打赏作者

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

抵扣说明:

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

余额充值