47 树Tree

二叉树性质
性质1: 在二叉树的第i层上至多有2i-1个结点(i>0)
性质2: 深度为k的二叉树至多有2k-1个结点(k>0)
性质3: 对于任何一棵二叉树,若2度的结点数有n2个,则叶子数(n0)必定为n2+1 (即n0=n2+1)
满二叉树:一棵深度为k 且有2k -1个结点的二叉树。 (特点:每层都“充满”了结点)

1 树的表示法

1 二叉链

// 二叉链表示法
/*
typedef struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/

struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;
void main01() {
	BiTNode t1, t2, t3, t4, t5;
	t1.data = 1;
	t2.data = 2;
	t3.data = 3;
	t4.data = 4;
	t5.data = 5;

	// 建立关系
	t1.lchild = &t2;
	t1.rchild = &t3;
	t2.lchild = &t4;
	t3.lchild = &t5;

	// 树的遍历
}
void main02() {
	BiTNode* p1, * p2, * p3, * p4, * p5;
	p1 = (BiTNode*)malloc(sizeof(BiTNode));
	p2 = (BiTNode*)malloc(sizeof(BiTNode));
	p3 = (BiTNode*)malloc(sizeof(BiTNode));
	p4 = (BiTNode*)malloc(sizeof(BiTNode));
	p5 = (BiTNode*)malloc(sizeof(BiTNode));

	p1->data = 1;
	p2->data = 2;
	p3->data = 3;
	p4->data = 4;
	p5->data = 5;


	// 建立关系
	p1->lchild = p2;
	p1->rchild = p3;
	p2->lchild = p4;
	p3->lchild = p5;

	// 树的遍历
}

2 三叉链

// 三叉链表
typedef struct TriTNode {
	int data;
	struct TriTNode* lchild, * rchild;
	struct TriTNode* parent;
}TriTNode, *TriTree;

3 双亲链表

// 双亲链表
#define MAX_TREE_SIZE 100
typedef	struct BPTNode {
	int data;
	int parentPosition;	// 指向双亲的指针		// 数组下标
	char LRTag;	// 左右孩子标志域
}BPTNode;

typedef struct BPTree {
	BPTNode nodes[100];//因为节点之间是分散的,需要把节点存储到数组中
	int num_node;	// 节点数目
	int root;	// 根节点的位置// 注意此域存储的是父亲节点在数组的下标
}BPTree;

void main() {
	BPTree tree;
	// 根节点
	tree.nodes[0].parentPosition = 1000;
	
	// B节点
	tree.nodes[1].parentPosition = 0;
	tree.nodes[1].data = 'B';
	tree.nodes[1].LRTag = 1;

	// C节点
	tree.nodes[2].parentPosition = 0;
	tree.nodes[2].data = 'C';
	tree.nodes[2].LRTag = 2;
}

2 树的遍历

1 先序遍历

先遍历根,再遍历左子树,再遍历右子树

2 中序遍历

先遍历左子树,再遍历根,再遍历右子树

3 后序遍历

先遍历左子树,再遍历右子树,再遍历根

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tHzRJHoh-1632112771959)(C:\Users\302\AppData\Roaming\Typora\typora-user-images\image-20210907101926430.png)]

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


// 二叉链表示法
/*
typedef struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/

struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;

void preOreder(BiTNode *root) {
	if (root == NULL) {
		return;
	}
	printf("%d ", root->data);
	// 遍历左子树
	preOreder(root->lchild);

	// 遍历右子树
	preOreder(root->rchild);

}

void midOreder(BiTNode* root) {
	if (root == NULL) {
		return;
	}

	// 遍历左子树
	midOreder(root->lchild);

	printf("%d ", root->data);

	// 遍历右子树
	midOreder(root->rchild);
}

void rearOreder(BiTNode* root) {
	if (root == NULL) {
		return;
	}

	// 遍历左子树
	rearOreder(root->lchild);
	// 遍历右子树
	rearOreder(root->rchild);

	printf("%d ", root->data);

	
}

void main() {
	BiTNode t1, t2, t3, t4, t5;
	memset(&t1, 0, sizeof(BiTNode));
	memset(&t2, 0, sizeof(BiTNode));
	memset(&t3, 0, sizeof(BiTNode));
	memset(&t4, 0, sizeof(BiTNode));
	memset(&t5, 0, sizeof(BiTNode));
	t1.data = 1;
	t2.data = 2;
	t3.data = 3;
	t4.data = 4;
	t5.data = 5;
	// 建立关系
	t1.lchild = &t2;
	t1.rchild = &t3;
	t2.lchild = &t4;
	t3.lchild = &t5;

	// 树的遍历
	printf("先序遍历:\n");
	preOreder(&t1);
	printf("\n");

	printf("中序遍历:\n");
	midOreder(&t1);
	printf("\n");

	printf("后序遍历:\n");
	rearOreder(&t1);
 }

结果

先序遍历:
1 2 4 3 5
中序遍历:
4 2 1 5 3
后序遍历:
4 2 5 3 1

4 树的非递归遍历(中序遍历)

栈模型

C++版本

#include<iostream>
using namespace std;
#include<stack>

/*
步骤1:
如果结点有左子树,该结点入栈;
如果结点没有左子树,访问该结点;
步骤2:
如果结点有右子树,重复步骤1;
如果结点没有右子树(结点访问完毕),根据栈顶指示回退,访问栈顶元素,并访问右子树,重复步骤1
如果栈为空,表示遍历结束。
*/

typedef struct BiTNode {
	int data;
	struct BiTNode* lchild;
	struct BiTNode* rchild;
}BiTNode,*BiTree;

// 找到中序遍历起点
BiTNode *goLeft(BiTNode* T, stack<BiTNode*>& s) {
	if (T == NULL) {
		return NULL;
	}
	// 判断T有无左子树,没有返回,若有继续
	while (T->lchild != NULL) {
		s.push(T);
		T = T->lchild;
	}
	return T;
}

void InOrder(BiTNode* T) {
	//
	BiTNode* t = NULL;
	stack<BiTNode*> s;

	t = goLeft(T, s);

	while (t) {
		printf("%d ", t->data);
		//如果t有右子树 重复步骤1
		if (t->rchild != NULL) {
			t = goLeft(t->rchild, s);	// 右子树中中序遍历起点
		}
		// 如果t没有右子树 根据栈顶指示 回退
		else if (!s.empty()) {
			t = s.top();
			s.pop();
		}
		// 如果没有右子树且栈为空
		else {
			t = NULL;
		}
	}
}

void midOrder(BiTNode* T) {
	if (T == NULL) {
		return;
	}
	// 遍历左子树
	midOrder(T->lchild);
	printf("%d ", T->data);
	// 遍历右子树
	midOrder(T->rchild);
}

void main() {
	BiTNode t1, t2, t3, t4, t5;
	memset(&t1, 0, sizeof(BiTNode));
	memset(&t2, 0, sizeof(BiTNode));
	memset(&t3, 0, sizeof(BiTNode));
	memset(&t4, 0, sizeof(BiTNode));
	memset(&t5, 0, sizeof(BiTNode));

	t1.data = 1;
	t2.data = 2;
	t3.data = 3;
	t4.data = 4;
	t5.data = 5;

	t1.lchild = &t2;
	t1.rchild = &t3;
	t2.lchild = &t4;
	t3.lchild = &t5;

	printf("中序遍历:\n");
	midOrder(&t1);
	cout << endl;

	cout << "中序遍历(非递归):\n";
	InOrder(&t1);
	cout << endl;

}

结果

中序遍历:
4 2 1 5 3
中序遍历(非递归):
4 2 1 5 3

C语言版本

#include<stdio.h>
#include<stdlib.h>
#include"linkstack.h"
/*
步骤1:
如果结点有左子树,该结点入栈;
如果结点没有左子树,访问该结点;
步骤2:
如果结点有右子树,重复步骤1;
如果结点没有右子树(结点访问完毕),根据栈顶指示回退,访问栈顶元素,并访问右子树,重复步骤1
如果栈为空,表示遍历结束。
*/

typedef struct BiTNode {
	int data;
	struct BiTNode* lchild;
	struct BiTNode* rchild;
}BiTNode, * BiTree;

// 找到中序遍历起点
BiTNode* goLeft(BiTNode* T, LinkStack* s) {
	if (T == NULL) {
		return NULL;
	}
	// 判断T有无左子树,没有返回,若有继续
	while (T->lchild != NULL) {
		LinkStack_Push(s, (void*)T);
		T = T->lchild;
	}
	return T;
}

void InOrder(BiTNode* T) {
	// 
	BiTNode* t = NULL;
	//stack<BiTNode*> s;
	LinkStack* s = LinkStack_Create();

	t = goLeft(T, s);

	while (t) {
		printf("%d ", t->data);
		//如果t有右子树 重复步骤1
		if (t->rchild != NULL) {
			t = goLeft(t->rchild, s);	// 右子树中中序遍历起点
		}
		// 如果t没有右子树 根据栈顶指示 回退
		//else if (!s.empty()) {
		else if(LinkStack_Size(s)>0){
			/*t = s.top();
			s.pop();*/
			t = (BiTNode*)LinkStack_Pop(s);
		}
		// 如果没有右子树且栈为空
		else {
			t = NULL;
		}
	}
}

void midOrder(BiTNode* T) {
	if (T == NULL) {
		return;
	}
	// 遍历左子树
	midOrder(T->lchild);
	printf("%d ", T->data);
	// 遍历右子树
	midOrder(T->rchild);
}

void main() {
	BiTNode t1, t2, t3, t4, t5;
	memset(&t1, 0, sizeof(BiTNode));
	memset(&t2, 0, sizeof(BiTNode));
	memset(&t3, 0, sizeof(BiTNode));
	memset(&t4, 0, sizeof(BiTNode));
	memset(&t5, 0, sizeof(BiTNode));

	t1.data = 1;
	t2.data = 2;
	t3.data = 3;
	t4.data = 4;
	t5.data = 5;

	t1.lchild = &t2;
	t1.rchild = &t3;
	t2.lchild = &t4;
	t3.lchild = &t5;

	printf("中序遍历:\n");
	midOrder(&t1);
	printf("\n");

	printf("中序遍历(非递归):\n");
	InOrder(&t1);
	printf("\n");

}

结果

中序遍历:
4 2 1 5 3
中序遍历(非递归):
4 2 1 5 3

3 #法创建树

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


// 二叉链表示法
/*
typedef struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/

struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;


void InOrder(BiTNode* T) {
	if (T == NULL) {
		return;
	}
	InOrder(T->lchild);
	printf("%d ", T->data);
	InOrder(T->rchild);
}

// 销毁树时用后续方法销毁
void FreeTree(BiTNode* T) {
	if (T == NULL) {
		return;
	}
	if (T->lchild != NULL) {
		FreeTree(T->lchild);
		T->lchild = NULL;
	}
	if (T->rchild != NULL) {
		FreeTree(T->rchild);
		T->rchild = NULL;
	}
	if (T != NULL) {
		free(T);
		T = NULL;
	}
}


// #法创建树 先序方法建立二叉链表
BiTNode* BiTree_Create() {
	BiTNode* tmp = NULL;
	char ch;
	scanf("%c", &ch);
	if (ch == '#') {
		return NULL;
	}
	else {
		tmp = (BiTNode*)malloc(sizeof(BiTNode));
		if (tmp == NULL) {
			return NULL;
		}
		tmp->data = ch;	//生成节点
		tmp->lchild = BiTree_Create();
		tmp->rchild = BiTree_Create();
	}
	return tmp;
}

void main() {
	int nCount = 0, depthval = 0;
	BiTNode* my = BiTree_Create();

	printf("\n 中序遍历 \n");
	InOrder(my);
	printf("\n");

	FreeTree(my);
}

结果

1#23###

 中序遍历
49 51 50

4 二叉线索树

希望可以像链表一样能够遍历树

结论:线索化过程就是在遍历过程(假设是中序遍历)中修改空指针的过程:

将空的lchild改为结点的直接前驱;

将空的rchild改为结点的直接后继。

可以把二叉树中序遍历结果存到链表里,再进行遍历,实现同样的效果

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

#define OK 1
#define ERROR -1
#define OVERFLOW 0
typedef enum{Link, Thread} PointerTag;
typedef char TElemType;
typedef int Status;

typedef struct _tag_BiThrNode {
	TElemType data;
	struct _tag_BiThrNode* lchild, * rchild;
	PointerTag LTag;
	PointerTag RTag;
}BiThrNode, * BiThrTree;


TElemType Nil = '#';

Status visit(TElemType e) {
	printf("%c", e);
	return OK;
}

// 按前序输入二叉线索树中节点的值,构造二叉	线索树T
// 0(整型)/空格(字符型)表示空节点
// 1##
Status CreateBiThrTree(BiThrTree* T) {
	TElemType h;
	scanf("%c", &h);

	if (h == Nil)
		* T = NULL;
	else {
		*T = (BiThrTree)malloc(sizeof(BiThrNode));
		if (!*T)
			exit(OVERFLOW);
		(*T)->data = h;	//生成根节点(前序)
		CreateBiThrTree(&(*T)->lchild);	//递归构造左子树
		if ((*T)->lchild)	//有左孩子
			(*T)->LTag = Link;
		CreateBiThrTree(&(*T)->rchild);	//递归构造右子树
		if ((*T)->rchild)	//有有孩子
			(*T)->RTag = Link;
	}
	return OK;
}

BiThrTree pre;	// 全局变量,始终指向刚刚访问过的节点
// 中序遍历进行中序线索化
void InThreading(BiThrTree p) {
	if (p) {
		InThreading(p->lchild);	// 递归左子树线索化
		if (!p->lchild)	// 没有左孩子
		{
			p->LTag = Thread;	//前驱线索
			p->lchild = pre; //左孩子指针指向前驱
		}
		if (!pre->rchild)	// 前驱没有右孩子
		{
			pre->RTag = Thread;	//后驱线索
			pre->rchild = p; //前驱右孩子指针指向后驱(当前节点p)
		}
		pre = p;//保持pre指向p的前驱
		InThreading(p->rchild);//递归右子树线索化
	}
}


/* 中序遍历二叉树T,并将其中序线索化,Thrt指向头节点*/
Status InOrderThreading(BiThrTree* Thrt, BiThrTree T) {
	*Thrt = (BiThrTree)malloc(sizeof(BiThrNode));
	if (!*Thrt)
		exit(OVERFLOW);
	(*Thrt)->LTag = Link;//建头节点
	(*Thrt)->RTag = Thread;
	(*Thrt)->rchild = (*Thrt);	//右指针回指
	if (!T)//若二叉树为空,则左指针回指
		(*Thrt)->lchild = *Thrt;
	else {
		(*Thrt)->lchild = T;
		pre = (*Thrt);
		InThreading(T);	//中序遍历进行中序线索化
		pre->rchild = *Thrt;
		pre->RTag = Thread;//最后一个节点线索化
		(*Thrt)->rchild = pre;
	}
	return OK;
}


// 中序遍历二叉线索树T(头节点)的非递归算法
Status InOrderTraverse_Thr(BiThrTree T) {
	BiThrTree p;
	p = T->lchild;	// p指向根节点
	while (p != T) {
		// 空树或遍历结束 p==T
		while (p->LTag == Link)p = p->lchild;
		if (!visit(p->data))	//访问其左子树为空的节点
			return ERROR;
		while (p->RTag == Thread && p->rchild != T) {
			p = p->rchild;
			visit(p->data);	//访问后继节点
		}
		p = p->rchild;
	}
	return OK;
}


int main() {
	BiThrTree H, T;
	printf("请按先序输入二叉树(如'ABDH##I##EJ###CF##G##')");
	CreateBiThrTree(&T);	//按先序产生二叉树
	InOrderThreading(&H, T);	// 中序遍历;并中序线索化二叉树
	printf("中序遍历(输出)二叉线索树:\n");
	InOrderTraverse_Thr(H);	//中序遍历输出二叉线索树
	printf("\n");
}

结果

请按先序输入二叉树(如'ABDH##I##EJ###CF##G##')ABDH##I##EJ###CF##G##
中序遍历(输出)二叉线索树:
HDIBJEAFCG

5 霍夫曼树

霍夫曼树

1.给定n个数值{ v1, v2, …, vn}

2.根据这n个数值构造二叉树集合F

F = { T1, T2, …, Tn}

Ti的数据域为vi,左右子树为空

3.在F中选取两棵根结点的值最小的树作为左右子树构造一棵新的二叉树,这棵二叉树的根结点中的值为左右子树根结点中的值之和

4.在F中删除这两棵子树,并将构造的新二叉树加入F中

5.重复3和4,直到F中只剩下一个树为止。这棵树即霍夫曼树

霍夫曼树是一种特殊的二叉树

霍夫曼树应用于信息编码和数据压缩领域

霍夫曼树是现代压缩算法的基础

案例1 计算叶子节点的个数

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


// 二叉链表示法
/*
typedef struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/

struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;

int sum = 0;

void coutLeaf(BiTNode* T) {
	if (T != NULL) {
		if (T->lchild == NULL && T->rchild == NULL) {
			sum++;
		}
		if (T->lchild) {
			coutLeaf(T->lchild);
		}
		if (T->rchild) {
			coutLeaf(T->rchild);
		}
	}
}

// 采用指针的方式,不使用全局变量
void coutLeaf2(BiTNode* T, int *sum) {
    // 采用前序求叶子节点,中序求叶子节点,后序求叶子节点结果一样
	if (T != NULL) {
		if (T->lchild == NULL && T->rchild == NULL) {
			(*sum)++;	// ++的优先级高
		}
		if (T->lchild) {
			coutLeaf2(T->lchild, sum);
		}
        /*
        if (T->lchild == NULL && T->rchild == NULL) {
			(*sum)++;	// ++的优先级高
		}
		*/
		if (T->rchild) {
			coutLeaf2(T->rchild, sum);
		}
        /*
        if (T->lchild == NULL && T->rchild == NULL) {
			(*sum)++;	// ++的优先级高
		}
		*/
	}
}

void main() {
	BiTNode t1, t2, t3, t4, t5;
	memset(&t1, 0, sizeof(BiTNode));
	memset(&t2, 0, sizeof(BiTNode));
	memset(&t3, 0, sizeof(BiTNode));
	memset(&t4, 0, sizeof(BiTNode));
	memset(&t5, 0, sizeof(BiTNode));
	t1.data = 1;
	t2.data = 2;
	t3.data = 3;
	t4.data = 4;
	t5.data = 5;
	// 建立关系
	t1.lchild = &t2;
	t1.rchild = &t3;
	t2.lchild = &t4;
	t3.lchild = &t5;

	coutLeaf(&t1);
	printf("sum:%d \n", sum);
	{
		int mysum = 0;
		coutLeaf2(&t1, &mysum);
		printf("sum:%d \n", mysum);
	}
	
 }

结果

sum:2
sum:2

案例2 求树的深度

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


// 二叉链表示法
/*
typedef struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/

struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;

int Depth(BiTNode *T) {
	int depthleft = 0;
	int depthright = 0;
	int depthval = 0;

	if (T == NULL) {
		depthval = 0;
		return depthval;
	}
	// 求左子树的高度
	depthleft = Depth(T->lchild);
	// 求右子树的高度
	depthright = Depth(T->rchild);
	
	// +1
	depthval = 1 + (depthleft > depthright ? depthleft : depthright);
	return depthval;
}

void main() {
	BiTNode t1, t2, t3, t4, t5;
	memset(&t1, 0, sizeof(BiTNode));
	memset(&t2, 0, sizeof(BiTNode));
	memset(&t3, 0, sizeof(BiTNode));
	memset(&t4, 0, sizeof(BiTNode));
	memset(&t5, 0, sizeof(BiTNode));
	t1.data = 1;
	t2.data = 2;
	t3.data = 3;
	t4.data = 4;
	t5.data = 5;
	// 建立关系
	t1.lchild = &t2;
	t1.rchild = &t3;
	t2.lchild = &t4;
	t3.lchild = &t5;

	printf("%d \n",Depth(&t1));
}

结果

3

案例3 copy Tree

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


// 二叉链表示法
/*
typedef struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
*/

struct BiTNode {
	int data;
	struct BiTNode* lchild, * rchild;
};
typedef struct BiTNode BiTNode;
typedef struct BiTNode* BiTree;

void midOreder(BiTNode* root) {
	if (root == NULL) {
		return;
	}

	// 遍历左子树
	midOreder(root->lchild);

	printf("%d ", root->data);

	// 遍历右子树
	midOreder(root->rchild);
}

BiTNode* CopyTree(BiTNode *T) {
	BiTNode* newNode = NULL;
	BiTNode* newLp = NULL;
	BiTNode* newRp = NULL;
	if (T == NULL) {
		return NULL;
	}
	// copy 左子树
	if (T->lchild != NULL) {
		newLp = CopyTree(T->lchild);	// copy左子树
	}
	else {
		newLp = NULL;
	}

	// copy 右子树
	if (T->rchild != NULL) {
		newRp = CopyTree(T->rchild);
	}
	else {
		newRp = NULL;
	}

	// malloc根节点
	newNode = (BiTNode*)malloc(sizeof(BiTNode));
	if (newNode == NULL) {
		return NULL;
	}
	newNode->lchild = newLp;
	newNode->rchild = newRp;
	newNode->data = T->data;

	return newNode;
}

void main() {
	BiTNode t1, t2, t3, t4, t5;
	memset(&t1, 0, sizeof(BiTNode));
	memset(&t2, 0, sizeof(BiTNode));
	memset(&t3, 0, sizeof(BiTNode));
	memset(&t4, 0, sizeof(BiTNode));
	memset(&t5, 0, sizeof(BiTNode));
	t1.data = 1;
	t2.data = 2;
	t3.data = 3;
	t4.data = 4;
	t5.data = 5;
	// 建立关系
	t1.lchild = &t2;
	t1.rchild = &t3;
	t2.lchild = &t4;
	t3.lchild = &t5;
    
	printf("midOrder:\n");
	midOreder(&t1);
	printf("\n");

	{
		BiTNode* root = CopyTree(&t1);
		printf("copy midOrder:\n");
		midOreder(root);
		printf("\n");
	}

}

结果

midOrder:
4 2 1 5 3
copy midOrder:
4 2 1 5 3

确定树的方法:通过中序遍历和先序遍历可以确定一个树

​ 通过中序遍历和后序遍历可以确定一个树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值