C语言(字符)二叉树的各种创建与遍历方法

目录

前言

代码与代码解释

头文件BinTreeH.h代码

头文件StackToBT.h代码

头文件QueueToBT.h代码

头文件BinTree.h代码

非递归实现二叉树的后序遍历解释

主函数代码

总代码与运行结果

总代码

 运行结果(仅展示一个样例)

 结语


前言

        嗯,最近在搞二叉树,因为个人觉得代码要保留下来,都是放在电脑里面又不好找,放多了会比较乱,所以就将一些学习过程中写的代码记录下来发表了,当然,虽然主要目的是记录,但是博主还是会很认真的将代码解释清楚的,毕竟不能误人子弟嘛。嗯。。。。因为博主想把创建二叉树的方法还有遍历方法都写下来,所以代码会比较长,于是博主就将代码分成几个文件来写了。

        如题嘛,这篇博客是介绍二叉树的创建与遍历的,还没有开始二叉树的插入、删除、查找这些哦,后面博主有时间也会写出来的,不过想看插入删除这些的也不用急着退出去哦,看看博主的代码或许也会有新的领悟的(实在是自夸了)。

代码与代码解释

        还有就是,因为写的是字符二叉树,所以博主就写了两种创建二叉树的方法:中序创建和层序创建。中序创建比较简单,是用递归实现的。

        下面就开始上代码了,必要的部分博主会讲解,比较简单的看一下注释就好了

头文件BinTreeH.h代码

//(字符)二叉树结构体定义的头文件
#include<stdio.h>
#include<stdlib.h>
typedef char BTElementType;//定义二叉树的元素类型
typedef struct TNode {//定义二叉树的结构体
	BTElementType Data;//树的数据域
	struct TNode* Left;//树的左指针,左子树
	struct TNode* Right;//树的右指针,右子树
	bool Visit;//布尔类型的变量,在后面用于二叉树的非递归遍历
}*BinTree;

头文件StackToBT.h代码

        这个头文件是定义堆栈

//用来服务二叉树的堆栈的头文件,堆栈元素为结构体结点
//堆栈定义为后入先出
#include"BinTreeH.h"
typedef BinTree ElementType;//将堆栈元素定义为结构体结点
typedef struct SNode {//定义堆栈结构体
	ElementType Data;
	struct SNode* Next;
}*Stack;
Stack CreateStack() {//创建堆栈
	Stack S = (Stack)malloc(sizeof(struct SNode));
	S->Next = NULL;//头节点空置
	return S;
}
bool IsEmptyS(Stack S) {//判断堆栈是狗为空
	return (S->Next == NULL);
}
void Push(Stack S,ElementType x) {//将堆栈元素入栈,头插法
	Stack p = (Stack)malloc(sizeof(struct SNode));
	p->Data = x;
	p->Next = S->Next;
	S->Next = p;
}
ElementType Pop(Stack S) {//将栈顶元素弹出,也就是将头节点的下一个结点弹出
	if (S->Next == NULL) return NULL;
	Stack p = S->Next;
	ElementType x = p->Data;
	S->Next = p->Next;
	free(p);//释放空间
	return x;
}

头文件QueueToBT.h代码

        这个头文件是定义队列

//用来服务二叉树实现的队列头文件,队列元素为结构体结点
//队列定义为先入先出
#include"StackToBT.h"
typedef BinTree ElementType;//将队列元素定义为结构体结点
typedef struct QNode {//定义队列结构体
	ElementType Data;
	struct QNode* Next;
}*Queue;
Queue CreateQueue() {
	Queue Q = (Queue)malloc(sizeof(struct QNode));
	Q->Next = NULL;//头节点空置
	return Q;
}
bool IsEmpty(Queue Q) {//判断队列是否为空
	return (Q->Next == NULL);
}
void AddQ(Queue Q, ElementType x) {//将元素入队,尾插法
	Queue q = (Queue)malloc(sizeof(struct QNode));
	q->Data = x;
	q->Next = NULL;
	if (IsEmpty(Q))Q->Next = q;//如果队列为空则直接将新的结点放到头节点后面
	else {
		Queue p = Q->Next;
		while (p->Next != NULL)p = p->Next;
		p->Next = q;
	}
}
ElementType DeleteQ(Queue Q) {
	if (IsEmpty(Q)) return NULL;//如果队列为空,直接返回空
	Queue p = Q->Next;
	ElementType x = p->Data;
	Q->Next = p->Next;
	free(p);
	return x;//将元素返回
}

头文件BinTree.h代码

        这个头文件才是大头,是拿来创建二叉树和遍历二叉树

        特别需要注意的是重载问题,博主用的编译软件是vs,如果导入自己写的头文件时,导入的头文件有重复的部分(也就是有的函数名一样这些问题),那么会报错“重载”,博主一开始也不知道问什么报错,被难了挺久,后来重新写才发现是这个错误(博主确实是菜鸡~)。

非递归实现二叉树的后序遍历解释

        比较难的就是后序遍历二叉树的非递归算法实现了,这里解释一下:

先简单讲一下我的大概思路,在定义二叉树结构体的时候我就定义了一个布尔类型的变量Visit,现在就是要用这一个变量来实现后序遍历,主体思路和前面的先序、中序遍历是一样的
就是在将每个结点压栈之前,将这个结点的Visit值(也就是结构体里定义的布尔值)赋为false在将一个结点的左子树遍历完之后,又返回到了这个结点,则将Visit变为true,并将这个结点重新压栈
当将该节点的右子树也访问完之后,再次返回这个结点,这时候Visit为true,则直接进行输出。

也就是说,每个结点都会遍历两遍才进行输出

这里将非递归实现后序遍历的代码单独截出来配合文字看:

void PostPrint2(BinTree BT) {//后序遍历的非递归实现
	Stack S = CreateStack();
	BinTree T = BT;
	while (T != NULL || !IsEmptyS(S)) {
		while (T != NULL) {//
			T->Visit = false;//将初入栈的结点的Visit值赋为false
			Push(S, T);//入栈
			T = T->Left;//往左移动
		}
		T = Pop(S);
		if (T->Visit == false) {//Visit为false,则将结点重新压栈
			T->Visit = true;
			Push(S, T);
			T = T->Right;//指向右子树(后面会进行)
		}
		else if (T->Visit == true) {//Visit为true,直接进行输出
			printf("%c", T->Data);
            T=NULL;//将T的元素输出后,将T赋为NULL,以跳过重新开始循环时找左节点的过程
		}
	}
}

 下面是整个的头文件代码

#include"QueueToBT.h"
//导入队列头文件
//只需要导入一个队列的头文件,因为队列的头文件中已经导入了堆栈的头文件
//而堆栈的头文件已经导入了定义二叉树结构体的头文件
//不能导入有重复部分的头文件,不然会报错:某函数不能重载

BinTree CreateBT() {//层序创建一颗二叉树
	//输入的字符为'0'表示为空结点
	BTElementType Data;
	scanf("%c", &Data);//输入根节点数据
	if (Data == '0')return NULL;
	BinTree BT = (BinTree)malloc(sizeof(struct TNode));
	BT->Data = Data;
	Queue Q = CreateQueue();//创建一个空队列
	AddQ(Q,BT);//根节点入队
	while (!IsEmpty(Q)) {//当队列不为空时继续循环
		BinTree T = DeleteQ(Q);//出队
		scanf("%c", &Data);//输入左子树数据
		if (Data != '0') {
			T->Left = (BinTree)malloc(sizeof(struct TNode));
			T->Left->Data = Data;
			AddQ(Q, T->Left);//将左子树入队
		}
		else T->Left = NULL;
		scanf("%c", &Data);//输入右子树数据
		if (Data != '0') {
			T->Right = (BinTree)malloc(sizeof(struct TNode));
			T->Right->Data = Data;
			AddQ(Q, T->Right);//将右子树入队
		}
		else T->Right = NULL;
	}
	return BT;//最后返回根节点(树根)
}
BinTree CreateBinTree(BinTree BT) {//中序创建二叉树
	BTElementType Data;
	scanf("%c", &Data);
	if (Data == '0')return NULL;
	else {
		BT = (BinTree)malloc(sizeof(struct TNode));
		BT->Data = Data;
		BT->Left=CreateBinTree(BT->Left);
		BT->Right = CreateBinTree(BT->Right);
	}
	return BT;
}
//下面是树的各种遍历方式
void PrePrint(BinTree BT) {//递归实现二叉树的先序遍历
	//先序:根->左子树->右子树
	if (BT != NULL) {
		printf("%c", BT->Data);
		PrePrint(BT->Left);
		PrePrint(BT->Right);
	}
}
void InPrint(BinTree BT) {//递归实现中序遍历二叉树
	//中序:左子树->根->右子树
	if (BT != NULL) {
		InPrint(BT->Left);
		printf("%c", BT->Data);
		InPrint(BT->Right);
	}
}
void PostPrint(BinTree BT) {//递归实现二叉树的后序遍历
	//后序:左子树->右子树->根
	if (BT != NULL) {
		PostPrint(BT->Left);
		PostPrint(BT->Right);
		printf("%c", BT->Data);
	}
}
void PrePrint2(BinTree BT) {//非递归实现先序遍历二叉树
	Stack S = CreateStack();//创建堆栈
	BinTree T = BT;
	while (T != NULL || !IsEmptyS(S)) {//当T不为NULL或堆栈不为空就继续进行循环
		//注:有一个成立就可以继续循环
		while (T != NULL) {//一直向左并将沿途结点压入堆栈
			printf("%c", T->Data);//先进行输出(先序遍历)
			Push(S,T);//压入堆栈
			T = T->Left;//指向左子树
		}
		T = Pop(S);//栈顶元素出栈
		T = T->Right;//转向右子树
	}
}
void InPrint2(BinTree BT) {//非递归实现中序遍历二叉树
	Stack S = CreateStack();
	BinTree T = BT;
	while(T!=NULL||!IsEmptyS(S)){
		while (T != NULL) {
			Push(S, T);
			T = T->Left;
		}
		T = Pop(S);
		printf("%c", T->Data);
		T = T->Right;
	}
}
void PostPrint2(BinTree BT) {//后序遍历的非递归实现
	Stack S = CreateStack();
	BinTree T = BT;
	while (T != NULL || !IsEmptyS(S)) {
		while (T != NULL) {//
			T->Visit = false;//将初入栈的结点的Visit值赋为false
			Push(S, T);//入栈
			T = T->Left;//往左移动
		}
		T = Pop(S);
		if (T->Visit == false) {//Visit为false,则将结点重新压栈
			T->Visit = true;
			Push(S, T);
			T = T->Right;//指向右子树(后面会进行)
		}
		else if (T->Visit == true) {//Visit为true,直接进行输出
			printf("%c", T->Data);
            T=NULL;
		}
	}
}
void LevelPrint(BinTree BT) {//层序遍历二叉树
	if (BT == NULL)return;
	BinTree T = BT;
	Queue Q = CreateQueue();//创建队列
	AddQ(Q, T);//将根节点入队
	while (!IsEmpty(Q)) {
		T = DeleteQ(Q);
		printf("%c", T->Data);
		if (T->Left != NULL)AddQ(Q, T->Left);//将左子树入队
		if (T->Right != NULL)AddQ(Q, T->Right);//将右子树入队
	}
}
//这个是将二叉树叶节点输出的递归算法,非递归就不写了
void PrePrintLeaves(BinTree BT) {//二叉树叶节点的输出算法
	if (BT != NULL) {
		if (!BT->Left && BT->Right) {
			printf("%c", BT->Data);
			PrePrintLeaves(BT->Left);
			PrePrintLeaves(BT->Right);
		}
	}
}

主函数代码

#include"BinTree.h"

int main() {
	BinTree bt = CreateBT();
	printf("递归实现先序遍历:\n");
	PrePrint(bt);
	printf("\n");
	printf("递归实现中序遍历:\n");
	InPrint(bt);
	printf("\n");
	printf("递归实现后序遍历:\n");
	PostPrint(bt);
	printf("\n");
	printf("非递归实现先序遍历:\n");
	PrePrint2(bt);
	printf("\n");
	printf("非递归实现中序遍历:\n");
	InPrint2(bt);
	printf("\n");
	printf("非递归实现后序遍历:\n");
	PostPrint2(bt);
	printf("\n");
	printf("最后是(非递归)层序的遍历:\n");
	LevelPrint(bt);
	printf("\n");
	return 0;
}

总代码与运行结果

        为了方便小伙伴们进行验证,博主专门将各个文件整合成了一个主函数文件,也有200+行了,所以求个赞赞吧!

总代码

#include<stdio.h>
#include<stdlib.h>
typedef char BTElementType;//定义二叉树的元素类型
typedef struct TNode {//定义二叉树的结构体
	BTElementType Data;//树的数据域
	struct TNode* Left;//树的左指针,左子树
	struct TNode* Right;//树的右指针,右子树
	bool Visit;//布尔类型的变量,在后面用于二叉树的非递归遍历
}*BinTree;
//---------堆栈部分
typedef BinTree ElementType;//将堆栈元素定义为结构体结点
typedef struct SNode {//定义堆栈结构体
	ElementType Data;
	struct SNode* Next;
}*Stack;
Stack CreateStack() {//创建堆栈
	Stack S = (Stack)malloc(sizeof(struct SNode));
	S->Next = NULL;//头节点空置
	return S;
}
bool IsEmptyS(Stack S) {//判断堆栈是狗为空
	return (S->Next == NULL);
}
void Push(Stack S, ElementType x) {//将堆栈元素入栈,头插法
	Stack p = (Stack)malloc(sizeof(struct SNode));
	p->Data = x;
	p->Next = S->Next;
	S->Next = p;
}
ElementType Pop(Stack S) {//将栈顶元素弹出,也就是将头节点的下一个结点弹出
	if (S->Next == NULL) return NULL;
	Stack p = S->Next;
	ElementType x = p->Data;
	S->Next = p->Next;
	free(p);//释放空间
	return x;
}
//--------------队列部分
typedef struct QNode {//定义队列结构体
	ElementType Data;
	struct QNode* Next;
}*Queue;
Queue CreateQueue() {
	Queue Q = (Queue)malloc(sizeof(struct QNode));
	Q->Next = NULL;//头节点空置
	return Q;
}
bool IsEmpty(Queue Q) {//判断队列是否为空
	return (Q->Next == NULL);
}
void AddQ(Queue Q, ElementType x) {//将元素入队,尾插法
	Queue q = (Queue)malloc(sizeof(struct QNode));
	q->Data = x;
	q->Next = NULL;
	if (IsEmpty(Q))Q->Next = q;//如果队列为空则直接将新的结点放到头节点后面
	else {
		Queue p = Q->Next;
		while (p->Next != NULL)p = p->Next;
		p->Next = q;
	}
}
ElementType DeleteQ(Queue Q) {
	if (IsEmpty(Q)) return NULL;//如果队列为空,直接返回空
	Queue p = Q->Next;
	ElementType x = p->Data;
	Q->Next = p->Next;
	free(p);
	return x;//将元素返回
}
//--------创建与遍历部分
BinTree CreateBT() {//层序创建一颗二叉树
	//输入的字符为'0'表示为空结点
	BTElementType Data;
	scanf("%c", &Data);//输入根节点数据
	if (Data == '0')return NULL;
	BinTree BT = (BinTree)malloc(sizeof(struct TNode));
	BT->Data = Data;
	Queue Q = CreateQueue();//创建一个空队列
	AddQ(Q, BT);//根节点入队
	while (!IsEmpty(Q)) {//当队列不为空时继续循环
		BinTree T = DeleteQ(Q);//出队
		scanf("%c", &Data);//输入左子树数据
		if (Data != '0') {
			T->Left = (BinTree)malloc(sizeof(struct TNode));
			T->Left->Data = Data;
			AddQ(Q, T->Left);//将左子树入队
		}
		else T->Left = NULL;
		scanf("%c", &Data);//输入右子树数据
		if (Data != '0') {
			T->Right = (BinTree)malloc(sizeof(struct TNode));
			T->Right->Data = Data;
			AddQ(Q, T->Right);//将右子树入队
		}
		else T->Right = NULL;
	}
	return BT;//最后返回根节点(树根)
}
BinTree CreateBinTree(BinTree BT) {//中序创建二叉树
	BTElementType Data;
	scanf("%c", &Data);
	if (Data == '0')return NULL;
	else {
		BT = (BinTree)malloc(sizeof(struct TNode));
		BT->Data = Data;
		BT->Left = CreateBinTree(BT->Left);
		BT->Right = CreateBinTree(BT->Right);
	}
	return BT;
}
//下面是树的各种遍历方式
void PrePrint(BinTree BT) {//递归实现二叉树的先序遍历
	//先序:根->左子树->右子树
	if (BT != NULL) {
		printf("%c", BT->Data);
		PrePrint(BT->Left);
		PrePrint(BT->Right);
	}
}
void InPrint(BinTree BT) {//递归实现中序遍历二叉树
	//中序:左子树->根->右子树
	if (BT != NULL) {
		InPrint(BT->Left);
		printf("%c", BT->Data);
		InPrint(BT->Right);
	}
}
void PostPrint(BinTree BT) {//递归实现二叉树的后序遍历
	//后序:左子树->右子树->根
	if (BT != NULL) {
		PostPrint(BT->Left);
		PostPrint(BT->Right);
		printf("%c", BT->Data);
	}
}
void PrePrint2(BinTree BT) {//非递归实现先序遍历二叉树
	Stack S = CreateStack();//创建堆栈
	BinTree T = BT;
	while (T != NULL || !IsEmptyS(S)) {//当T不为NULL或堆栈不为空就继续进行循环
		//注:有一个成立就可以继续循环
		while (T != NULL) {//一直向左并将沿途结点压入堆栈
			printf("%c", T->Data);//先进行输出(先序遍历)
			Push(S, T);//压入堆栈
			T = T->Left;//指向左子树
		}
		T = Pop(S);//栈顶元素出栈
		T = T->Right;//转向右子树
	}
}
void InPrint2(BinTree BT) {//非递归实现中序遍历二叉树
	Stack S = CreateStack();
	BinTree T = BT;
	while (T != NULL || !IsEmptyS(S)) {
		while (T != NULL) {
			Push(S, T);
			T = T->Left;
		}
		T = Pop(S);
		printf("%c", T->Data);
		T = T->Right;
	}
}
void PostPrint2(BinTree BT) {//后序遍历的非递归实现
	Stack S = CreateStack();
	BinTree T = BT;
	while (T != NULL || !IsEmptyS(S)) {
		while (T != NULL) {//
			T->Visit = false;//将初入栈的结点的Visit值赋为false
			Push(S, T);//入栈
			T = T->Left;//往左移动
		}
		T = Pop(S);
		if (T->Visit == false) {//Visit为false,则将结点重新压栈
			T->Visit = true;
			Push(S, T);
			T = T->Right;//指向右子树(后面会进行)
		}
		else if (T->Visit == true) {//Visit为true,直接进行输出
			printf("%c", T->Data);
            T=NULL;
		}
	}
}
void LevelPrint(BinTree BT) {//层序遍历二叉树
	if (BT == NULL)return;
	BinTree T = BT;
	Queue Q = CreateQueue();//创建队列
	AddQ(Q, T);//将根节点入队
	while (!IsEmpty(Q)) {
		T = DeleteQ(Q);
		printf("%c", T->Data);
		if (T->Left != NULL)AddQ(Q, T->Left);//将左子树入队
		if (T->Right != NULL)AddQ(Q, T->Right);//将右子树入队
	}
}
//这个是将二叉树叶节点输出的递归算法,非递归就不写了
void PrePrintLeaves(BinTree BT) {//二叉树叶节点的输出算法
	if (BT != NULL) {
		if (!BT->Left && BT->Right) {
			printf("%c", BT->Data);
			PrePrintLeaves(BT->Left);
			PrePrintLeaves(BT->Right);
		}
	}
}
//-------主函数部分
int main() {
	BinTree bt = CreateBT();
	printf("递归实现先序遍历:\n");
	PrePrint(bt);
	printf("\n");
	printf("递归实现中序遍历:\n");
	InPrint(bt);
	printf("\n");
	printf("递归实现后序遍历:\n");
	PostPrint(bt);
	printf("\n");
	printf("非递归实现先序遍历:\n");
	PrePrint2(bt);
	printf("\n");
	printf("非递归实现中序遍历:\n");
	InPrint2(bt);
	printf("\n");
	printf("非递归实现后序遍历:\n");
	PostPrint2(bt);
	printf("\n");
	printf("最后是(非递归)层序的遍历:\n");
	LevelPrint(bt);
	printf("\n");
	return 0;
}

 运行结果(仅展示一个样例)

 结语

        呜呼~!花了一个下午终于将这篇博客写完了(主要是在写代码),如果小伙伴们发现代码有误或者其它问题,欢迎进行评论提问哦!博主看到会回复的。

  • 9
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,下面给您介绍一下如何使用 C语言创建二叉树遍历二叉树。 首先,我们需要定义二叉树节点的结构体: ```c typedef struct TreeNode { char val; struct TreeNode *left; struct TreeNode *right; } TreeNode; ``` 其中,`val` 表示节点的值,`left` 和 `right` 分别表示左子节点和右子节点。 接下来,我们可以使用递归的方式来构建二叉树。以输入的中序序列和后序序列为例,具体步骤如下: 1. 定义一个递归函数 `buildTree()`,该函数接受两个参数,分别为中序序列和后序序列的起始和结束位置。 2. 在后序序列中找到最后一个字符,即为根节点的值。 3. 在中序序列中找到根节点的位置,将中序序列分成左右两个部分。 4. 递归构建左子树和右子树。 5. 返回根节点。 具体的代码实现如下: ```c TreeNode* buildTree(char* inorder, int inStart, int inEnd, char* postorder, int postStart, int postEnd) { if (inStart > inEnd || postStart > postEnd) { return NULL; } char rootVal = postorder[postEnd]; int rootIndex; for (int i = inStart; i <= inEnd; i++) { if (inorder[i] == rootVal) { rootIndex = i; break; } } int leftSize = rootIndex - inStart; TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode)); root->val = rootVal; root->left = buildTree(inorder, inStart, rootIndex - 1, postorder, postStart, postStart + leftSize - 1); root->right = buildTree(inorder, rootIndex + 1, inEnd, postorder, postStart + leftSize, postEnd - 1); return root; } ``` 接下来,我们可以使用先序遍历、中序遍历和后序遍历的方式遍历二叉树。以先序遍历为例,具体步骤如下: 1. 如果节点为空,返回。 2. 访问当前节点。 3. 递归遍历左子树。 4. 递归遍历右子树。 具体的代码实现如下: ```c void preorderTraversal(TreeNode* root) { if (root == NULL) { return; } printf("%c", root->val); preorderTraversal(root->left); preorderTraversal(root->right); } ``` 最后,我们可以将上述代码整合起来,得到完整的程序: ```c #include <stdio.h> #include <stdlib.h> typedef struct TreeNode { char val; struct TreeNode *left; struct TreeNode *right; } TreeNode; TreeNode* buildTree(char* inorder, int inStart, int inEnd, char* postorder, int postStart, int postEnd) { if (inStart > inEnd || postStart > postEnd) { return NULL; } char rootVal = postorder[postEnd]; int rootIndex; for (int i = inStart; i <= inEnd; i++) { if (inorder[i] == rootVal) { rootIndex = i; break; } } int leftSize = rootIndex - inStart; TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode)); root->val = rootVal; root->left = buildTree(inorder, inStart, rootIndex - 1, postorder, postStart, postStart + leftSize - 1); root->right = buildTree(inorder, rootIndex + 1, inEnd, postorder, postStart + leftSize, postEnd - 1); return root; } void preorderTraversal(TreeNode* root) { if (root == NULL) { return; } printf("%c", root->val); preorderTraversal(root->left); preorderTraversal(root->right); } int main() { char inorder[] = "DCBGEAHFIJK"; char postorder[] = "DCEGBFHKJIA"; TreeNode* root = buildTree(inorder, 0, 10, postorder, 0, 10); printf("先序遍历结果:"); preorderTraversal(root); printf("\n"); return 0; } ``` 输出结果为: ``` 先序遍历结果:ABCDGEIHFJK ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xiaoyuer2815

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

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

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

打赏作者

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

抵扣说明:

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

余额充值