浙江大学《数据结构》课程小结

一、线性结构

以一元多项式及其运算为例:
f(x) = a0+a1x+…+anxn
1.顺序存储结构直接表示
在这里插入图片描述
多项式相加:直接相加
缺点:零项过多!占空间
2.顺序存储结构表示非零项
每个非零项涉及两个参数:系数和指数,所以可使用结构数组按指数大小有序存储表示
多项式相加:对应指数相加
3.链表结构存储非零项
链表中每个结点村塾多项式中的一个非零项,包括系数和指数两个数据域以及一个指针域
在这里插入图片描述

typedef struct PolyNode *Polynomial;
struct PolyNode
{
	int coef;
	int expon;
	Polynomial link;
}

链表的插入(O(n)) 删除(O(n) 也可以O(1) 把下一个结点的值赋予该结点 删除下一个结点)
广义表:二元多项式
多重链表:图(邻接表)

二、树

1.概念:
结点的度:结点的子树个数
树的度:树种所有结点中最大的度
树的深度:树中所有结点中的最大层次是这棵树的深度 只有根节点 层次为1
2.二叉树性质与遍历:
完全二叉树:结点序号位置同满二叉树
对任何非空二叉树T,若n0表示叶节点个数 n1表示度为1的结点个数,n2表示度为2的非叶结点个数,那么根据二叉树边相等 存在no+n1+n2-1 = 2n2+n1 即n0 = n2+1。
先序遍历 中序遍历 后序遍历的递归写法与非递归写法
层序遍历(队列、堆栈)
已知中序遍历和另外一种 可唯一确定树
3.二叉搜索树:

BinTree Insert( BinTree BST, ElementType X )
{
    if( !BST ){ /* 若原树为空,生成并返回一个结点的二叉搜索树 */
        BST = (BinTree)malloc(sizeof(struct TNode));
        BST->Data = X;
        BST->Left = BST->Right = NULL;
    }
    else { /* 开始找要插入元素的位置 */
        if( X < BST->Data )
            BST->Left = Insert( BST->Left, X );   /*递归插入左子树*/
        else  if( X > BST->Data )
            BST->Right = Insert( BST->Right, X ); /*递归插入右子树*/
        /* else X已经存在,什么都不做 */
    }
    return BST;
}

BinTree Delete( BinTree BST, ElementType X ) 
{ 
    Position Tmp; 

    if( !BST ) 
        printf("要删除的元素未找到"); 
    else {
        if( X < BST->Data ) 
            BST->Left = Delete( BST->Left, X );   /* 从左子树递归删除 */
        else if( X > BST->Data ) 
            BST->Right = Delete( BST->Right, X ); /* 从右子树递归删除 */
        else { /* BST就是要删除的结点 */
            /* 如果被删除结点有左右两个子结点 */ 
            if( BST->Left && BST->Right ) {
                /* 从右子树中找最小的元素填充删除结点 */
                Tmp = FindMin( BST->Right );
                BST->Data = Tmp->Data;
                /* 从右子树中删除最小元素 */
                BST->Right = Delete( BST->Right, BST->Data );
            }
            else { /* 被删除结点有一个或无子结点 */
                Tmp = BST; 
                if( !BST->Left )       /* 只有右孩子或无子结点 */
                    BST = BST->Right; 
                else                   /* 只有左孩子 */
                    BST = BST->Left;
                free( Tmp );
            }
        }
    }
    return BST;
}

4.平衡二叉树:
平衡因子:BF(T) = hl - hr;
平衡二叉树为空树或者任一结点左、右子树高度差的绝对值不超过1,即|BF(T)| <= 1
LR RR RL LL (麻烦结点相对于发现者的位置)
T是不平衡的发现者, 根据麻烦结点在发现者的位置 确定插入方式

// Root of AVL Tree
// LL RR LR RL 所有对AVL树的调整即可归结到这四种方法
#include <iostream>
using namespace std;
typedef struct TreeNode * Tree;
struct TreeNode
{
    int value;
    Tree left, right; 
};

Tree maketree(int n); // 建立AVL 动态调整
Tree newnode(int v); // 申请新节点
Tree insert(Tree T, int v); //向树种插入元素
int height(Tree T); //计算一个树的高度
Tree LLrotation(Tree T);
Tree LRrotation(Tree T);
Tree RLrotation(Tree T);
Tree RRrotation(Tree T);

int main()
{
    int n;
    cin >> n;
    Tree T;
    T = maketree(n);
    cout << T->value<<endl;

}
Tree maketree(int n)
{
    int v;
    cin >> v;
    Tree T = newnode(v);
    for(int i = 1; i < n; i++)
    {
        cin >> v;
        T = insert(T,v);
    }
    return T;
}
Tree newnode(int v)
{
    Tree  T = new(struct TreeNode);
    T->value = v;
    T->left = T->right = nullptr;
    return T;
}
Tree insert(Tree T, int v)
{
    if(!T) T = newnode(v);
    else
    {
        if(v < T->value)
        {
            T->left = insert(T->left,v);
            if(height(T->left) - height(T->right) == 2)
            {
                if( v < T->left->value) // LL
                    T = LLrotation(T);
                else
                    T = LRrotation(T);
            }
        }
        else
        {
            T->right = insert(T->right,v);
            if(height(T->right) - height(T->left) == 2)
            {
                if( v > T->right->value) // rr
                    T = RRrotation(T);
                else
                    T = RLrotation(T);
            }
        }
    }
    return T;

}
Tree LLrotation(Tree T)
{
	Tree a = T, b = T->left;//记录原来根节点,根节点的左节点
	//Tree bl=b->left, br=b->right, ar=a->right;//记录三个需要旋转的树
	a->left = b->right;
	b->right = a;
	return b;
}
Tree LRrotation(Tree T)
{
	Tree a = T, b = T->left, c = b->right;//记录原来根节点,根节点的左节点
	//Tree bl=b->left, br=b->right, ar=a->right;//记录三个需要旋转的树
    b->right = c->left;
    c->left = b;
    a->left = c->right;
    c->right = a;

	return c;
}
Tree RRrotation(Tree T)
{
	Tree a = T, b = T->right;//记录原来根节点,根节点的左节点
	//Tree bl=b->left, br=b->right, ar=a->right;//记录三个需要旋转的树
	a->right = b->left;
	b->left = a;
	return b;
}
Tree RLrotation(Tree T)
{
	Tree a = T, b = T->right, c = b->left;//记录原来根节点,根节点的左节点
	//Tree bl=b->left, br=b->right, ar=a->right;//记录三个需要旋转的树
    b->left = c->right;
    c->right = b;
    a->right = c->left;
    c->left = a;

	return c;
}
int height(Tree T)
{
    int hl,hr,max;
    if(T)
    {
        hl = height(T->left);
        hr = height(T->right);
        max = hl > hr?hl:hr;
        return max+1;
    }
    else
        return 0;
}

5.堆:
1.用数组表示的完全二叉树
2.任一结点的关键字是其子树所有结点的最值
插入:向上过滤

typedef struct HNode *Heap; /* 堆的类型定义 */
struct HNode {
    ElementType *Data; /* 存储元素的数组 */
    int Size;          /* 堆中当前元素个数 */
    int Capacity;      /* 堆的最大容量 */
};
typedef Heap MaxHeap; /* 最大堆 */
typedef Heap MinHeap; /* 最小堆 */

#define MAXDATA 1000  /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */

MaxHeap CreateHeap( int MaxSize )
{ /* 创建容量为MaxSize的空的最大堆 */

    MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
    // 数组
    H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType));
    H->Size = 0;
    H->Capacity = MaxSize;
    H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/

    return H;
}

bool IsFull( MaxHeap H )
{
    return (H->Size == H->Capacity);
}

bool Insert( MaxHeap H, ElementType X )
{ /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
// 本质是将插入元素放到最后一个位置 然后自下向上过滤
    int i;
 
    if ( IsFull(H) ) { 
        printf("最大堆已满");
        return false;
    }
    i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
    for ( ; H->Data[i/2] < X; i/=2 )
        H->Data[i] = H->Data[i/2]; /* 向下向上过滤X */
    H->Data[i] = X; /* 将X插入 */
    return true;
}

#define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */

bool IsEmpty( MaxHeap H )
{
    return (H->Size == 0);
}

ElementType DeleteMax( MaxHeap H )
{ /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
/* 本质上是将堆的最后一个元素 放到堆的顶部 然后自上向下过滤*/
    int Parent, Child;
    ElementType MaxItem, X;

    if ( IsEmpty(H) ) {
        printf("最大堆已为空");
        return ERROR;
    }

    MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
    /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
    X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
    // Parent*2<=H->Size 判别有无左儿子
    for( Parent=1; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;

    return MaxItem;
} 

/*----------- 建造最大堆 -----------*/
void PercDown( MaxHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    ElementType X;

    X = H->Data[p]; /* 取出根结点存放的值 */
    for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;
}

void BuildHeap( MaxHeap H )
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
  /* 这里假设所有H->Size个元素已经存在H->Data[]中 */

    int i;
	//思路:自下向上的 向下过滤 哈哈哈
    /* 从最后一个结点的父节点开始,到根结点1 */
    for( i = H->Size/2; i>0; i-- ) 
        PercDown( H, i );
}

三、图

1.遍历
DFS(递归) BFS(队列)
2.最短路径

// 无权图的单源最短路算法, 每个边的权重都是1 非递减计算 类似于BFS
void Unweighted(Vertex S)
{
	Enqueue(S,Q);
	while(!IsEmpty(Q))
	{
	V = Dequeue(Q);
	for ( V的每个邻接点W)
		if(dist[W] == -1)
		{
		dist[w] = dist[V]+1; //S 到每个顶点W的最短路径
		path[W] = V;//最短路径 
		Enqueue(W,Q);
		}

	}

}

在这里插入图片描述

//不能有负值圈  Dijkstra
// 有权图的单源最短路算法, 非递减计算 类似于BFS
void Dijkstra(Vertex S)
{
while(1)
{
	V = 未收录顶点中dist最小者;
	if V不存在
		break;
	collected[V] = true;
	for(V的每个邻接点W)
		if(collected[W] == false)
			if(dist[V]+E < dist[W])
			{
			dist[W]  = dist[V]+E;
			path[W] = V;
			}

}

}

在这里插入图片描述

void Floyd()
{
for(i = 0; i < N; i++)
	for(j = 0; j < N; j++)
	{
	D[i][j] = G[i][j];
	path[i][j] = -1;
	}
for(k = 0; k < N; k++)
	for(i = 0;i<N; i++)
		for(j = 0; j < N; j++)
		if(D[i][k] +D[k][j]<D[i][j])
		{
		D[i][j] = D[i][k]+D[k][j];
		path[i][j] = k;
		}
}

3.最小生成树
Prim :让一颗小树慢慢长大

void Prim()
{
MST = {s};
while(1)
{
V = 未收录顶点中dist最小的 (dist是顶点V离最小生成树的距离)
if(V不存在)
	break;
将V收录进MST;dist[V] = 0;
for(V的每个邻接点W)
	if(W未被收录)dist[W]!=0
		if(E(v,w) < dist[W])
		{
		dist[W] = E(v,w);
		parent[W] = V;
		}
}
if(MST中的顶点不足V个)
	Error;
}

Kruskal :将森林合并成树

void Kruskal(Graph G)
{
MST = {};
while(MST中不到|V| -1条边 && E中还有边)
{
从E中取一条权重最小的边E(v,w); //最小堆E(v,w)从E中删除; 
if(E(v,w)不在MST中构成回路) //并查集E(v,w) 加入MST;
}
if(MST中的不足V-1个边)
	Error;
}

4.拓扑排序

void Topsort( Graph G )
{
   Queue Q;
   Vertex V, W;
   NodePtr ptr;
   int counter = 0;

   Q = CreateEmptyQueue(NumVertex);
   for ( V=0; V<G->NumV; V++ )
      if ( Indegree[V] == 0 )
         Enqueue(V, Q);
   while ( !IsEmpty(Q) ){
      V = Dequeue( Q );
      TopNum[V] = counter++;
      for ( ptr=G->List[V]; ptr; ptr=ptr->Next) {
         W = ptr->Vertex;
         if ( --Indegree[W]== 0 )
            Enqueue(W, Q);
      }
   }
   if ( counter != NumVertex )
      printf("ERROR: Graph has a cycle.\n");
   DisposeQueue(Q);
}

四、排序

// 冒泡排序  泡泡泡泡 我的泡泡

    void Bubble_sort(ElementType A[], int N)
    {
        for(P = N-1; P>= 0; P--)
        {
            flag = 0;
            for(i = 0; i < P; i++) // P是最后一个未冒泡元素的位置  一趟冒泡
            {
                if(A[i] > A[i+1]) //稳定
                {
                    Swap(A[i],A[i+1]);
                    flag = 1;
                }
            }
            if(flag == 0)    break;
        }
    }

    性能比较:最好 O(N)  最坏O(N2)



// 插入排序 

    void Insertion_Sort(ElementType A[], int N)
    {
        for( P = 1; p < N; P++)
        {
            Tmp = A[P]; // 摸下一张牌
            for(i = P; i > 0 && A[i-1] > Tmp; i--) // 往前面已排好的队里插
                A[i] = A[i-1]; // 移出空位
            A[i] = Tmp;// 新牌落位
        }
    }
    最好顺序 O(N)  逆序 O(N^2)


// 时间复杂度下界

    对于下标i<j,如果A[i] > A[j],则称(i,j)是一对逆序对
    每次交换元素时,正好削去1个逆序对
    所以插入排序 T(N,I) = O(N+I) 如果序列基本有序,则插入排序简单高效
    定理:任何仅以交换相邻两元素来排序的算法,其平均时间复杂度未Ω(N^2)
    优化点:交换相邻较远的两元素(可以大量减少逆序对)

// 希尔排序

    void Shell_sort(ElementType A[], int N)
    {
        for (D = N/2;  D > 0; D /= 2) //希尔增量序列
        {
            for(P = D; P<N; P++) //第0张牌在我手里 我从第D张牌开始摸
            {
                Tmp = A[P];
                for(i = P; i >= D && A[i-D]>Tmp; i -= D)
                    A[i] = A[i-D];
                A[i] = Tmp;
            }
        }
    }


// 选择排序

    void Selection_Sort(ElementType A[], int N)
    {
        for( i = 0; i < N; i++)
        {
            MinPosition = ScanForMin(A, i, N-1);
            // 从 A[i]到A[N-1]中找最小元,并将其位置赋给MinPosition **这一步浪费时间 最小堆
            Swap(A[i], A[MinPosition]);
            // 将未排序部分的最小元换到有序部分的最后位置
        }
    }

// 堆排序 

    算法1//需要额外存储空间
    void Heap_Sort(ElementType A[], int N)
    {
        BuildHeap(A);
        for (i = 0 ; i < N; i++)
            TmpA[i] = DeleteMin(A);
        for( i = 0; i < N; i++)
            A[i] = TmpA[i]; 
    }
    算法2void PercDown( MaxHeap H, int p )
{ 
    int Parent, Child;
    ElementType X;

    X = H->Data[p]; 
    for( Parent=p; Parent*2<=H->Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) )
            Child++;  
        if( X >= H->Data[Child] ) break; 
        else  
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;
}

    void Heap_Sort(ElementType A[], int N)
    {
        for( i = N/2; i >= 0; i--)
            PercDown(A, i, N); //向下过滤 构造最大堆
        for(i = N-1; i > 0; i--)
        {
            Swap(&A[0], &A[i]);// A[0]是最大的 与A[i]当前堆的最后一个交换
            PercDown(A, 0, i); //因当前堆的最后一个位置已固定 所以仅需调节前面的节点成最大堆即可
        }
    }

// 归并排序

    // 一个有序数组A[]的左部分喝右部分  合并
    void Merge(ElementType A[], ElementType TmpA[], int L, int R, int RightEnd)
    {
        LeftEnd = R - 1; // 左边终点位置。假设左右两列挨着
        Tmp = L; // 存放结果的数组初始位置
        NumElements = RightEnd - L + 1; // 总元素数量
        while( L <= LeftEnd && R <= RightEnd) 
        {
            if(A[L] <= A[R]) TmpA[Tmp++] = A[L++];
            else TmpA[Tmp++] = A[R++];
        }
        while(L <= LeftEnd)
            TmpA[Tmp++] = A[L++];
        while(R <= RightEnd)
            TmpA[Tmp++] = A[R++];
        for(i = 0; i < NumElements; i++, RightEnd--)
            A[RightEnd] = TmpA[RightEnd];
    }

    void MSort(ElementType A[], ElementType TmpA[],int L, int RightEnd)
    {
        int Center;
        if (L < RightEnd)
        {
            Center = ( L + RightEnd) /2;
            MSort(A, TmpA, L, Center); //左边规整
            MSort(A, TmpA, Center+1, RightEnd); //右边规整
            Merge(A,TmpA,L,Center+1,RightEnd); //合并
            
        }
    }
    void Merge_sort(ElemntType A[], int N)
    {
        ElementTyoe *TmpA;
        TmpA = malloc (N * sizeof(ElementType));
        if(TmpA != NULL)
        {
            MSort(A,TmpA,0,N-1);
            free(TmpA);
        }
    }
//非递归写法
    void Merge_pass(ElementType A[], ElementType TmpA[], int N, int length)
    { //length= 当前有序子列的长度
        for(i = 0; i <= N-2*length; i+= 2*length)
            Merge1(A,TmpA, i,i+length,i+2*length-1);//归并到TmpA;
        if(i+length < N)
            Mergel(A,Tmp,i,i+length,N-1);
        else
            for(j = i; j < N;j++) TmpA[j] = A[j];

    }
    void Merge_sort(ElemntType A[], int N)
    {
        int length=1;
        ElementTyoe *TmpA;
        TmpA = malloc (N * sizeof(ElementType));
        if(TmpA != NULL)
        {
            while(length < N)
            {
                Merge_pass(A,TmpA,N,length);
                length *= 2;
                Merge_pass(TmpA,A,N,length);
                length *= 2;
            }
            
            free(TmpA);
        }
    }
*/
void Quicksort(ElementType A[], int N)
{
    pivot = 从A[]中选一个主元(基准);
    A1 = {a<= pivot} ; a属于S(S为A中删除pivot的集合)
    A2 = {a> pivot} ;
    A[] = Quicksort(A1,N1)+pivot+Quicksort(A2,N2);
}
pivot 的选取可取头部、中部、尾部的中位数
ElementType Median3(ElementType A[], int left, int Right)
{
    int center = (left + right ) / 2;
    if( A[left] > A[center])
        Swap(&A[left], &A[center]);
    if(A[left] > A[right])
        Swap(&A[left], &A[right]);  // left是最小的
    if(A[center] > A[right])
        Swap(&A[center], &A[right]);  // center是中部
    Swap(&A[center], &A[right-1]); //将pivot藏到右边
    // 由于 left必定比center小 而right必定比center大
    // 所以直接排 left+1 right-2即可(pivot放在right-1)
    return A[right-1];
}

void Quicksort(ElementType A[], int left ,int right)
{
    pivot = Median(A,left,right);
    i = left; j = right-1;
    for(; ;)
    {
        while(A[++i] < Pivot) {}
        while(A[--j] > Pivot) {}
        if(i < j)
            Swap(&A[i],&A[j]);
        else break;
    }
    Swap(&A[i],&A[right-1]);
    Quicksort(A,left,i-1);
    Quicksort(A,i+1,right);
    
}
*/

/* 表排序 间接排序· 只对索引*/

/* 基数排序*/

//桶排序 
void Bucket_Sort(ElementType A[], int N)
{
    count[] 初始化;
    while(读入1个学生成绩grade);
        将该生插入count[grade]链表;
    for(i = 0; i < M; i++)
    {
        if(count[i])
            输出整个coount[i]链表 
    }
}

//基数排序
#define MaxDigit 4
#define Radix 10 // 十进制


typedef struct Node *PtrToNode;
struct Node {
    int key;
    PtrToNode next;
};


struct HeadNode {
    PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];

// 得出X所在位数D上的数字
int GetDigit ( int X, int D )
{ 
    int d, i;
    
    for (i=1; i<=D; i++) {
        d = X % Radix;
        X /= Radix;
    }
    return d;
}

void LSDRadixSort( ElementType A[], int N )
{ 
     int D, Di, i;
     Bucket B;
     PtrToNode tmp, p, List = NULL; 
     
     // 由于是十进制 所以放置10个桶 初始每个桶所存链表的头指针和尾指针都为空
     for (i=0; i<Radix; i++)
         B[i].head = B[i].tail = NULL;
    // 将数组转为链表存储(倒置了) 最后list是该链表的头节点 其内容是A[N-1]
     for (i=0; i<N; i++) { 
         tmp = (PtrToNode)malloc(sizeof(struct Node));
         tmp->key = A[i];
         tmp->next = List;
         List = tmp;
     }

     for (D=1; D<=MaxDigit; D++) { 

         p = List;//顺序的确定依据这一句 原因list已将上一批次桶串接
         while (p) {
             Di = GetDigit(p->key, D); 

             tmp = p; p = p->next;
          
             tmp->next = NULL;
             // 如果该桶是空的 则该链表的头指针和尾指针都是tmp 否则再已有链表后追加
             if (B[Di].head == NULL)
                 B[Di].head = B[Di].tail = tmp;
             else {
                 B[Di].tail->next = tmp;
                 B[Di].tail = tmp;
             }
         }
         // 在每一位的排序中(每一排桶 串接起来)
         List = NULL; 
         for (Di=Radix-1; Di>=0; Di--) { 
             if (B[Di].head) { 
              
                 B[Di].tail->next = List;
                 List = B[Di].head;
                 B[Di].head = B[Di].tail = NULL;
             }
         }
     }
     
     for (i=0; i<N; i++) {
        tmp = List;
        List = List->next;
        A[i] = tmp->key;
        free(tmp);
     } 
}

五、散列

散列函数构造:
1.直接定址法
2.除留余数法:h(key) = key mod p p一般为>=表长的素数
3.折叠法 平方取中法
冲突处理:
开放地址法:
线性探测、平方探测、双散列、再散列(加倍扩大散列表)
分离链接法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值