常见算法总结

常见算法和数据结构总结

一、基本算法思想

1.贪心算法
将一个大问题分成若干个子问题,求子问题最优解,然后把子问题的解合并成原来问题的解。

例题:小明手中有 1,5,10,50,100 五种面额的纸币,每种纸币对应张数分别为 5,2,2,3,5 张。若小明需要支付 456 元,则需要多少张纸币?

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

const int N = 5;
int Count[N] = { 5,2,2,3,5 };//每一张纸币的数量 
int Value[N] = { 1,5,10,50,100 };
int solve(int money) {
	int num = 0;
	for (int i = N - 1; i >= 0; i--) {
		int c = min(money / Value[i], Count[i]);//每一个所需要的张数 
		money = money - c*Value[i];
		num += c;//总张数 
	}
	if (money>0) num = -1;
	return num;
}

int main()
{
	cout << solve(456) << endl;
	system("pause");
	return 0;
}

2.动态规划
是一种空间换取时间的算法,一句话解释就是记住你之前得到的答案。

例题:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。

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

int F(int n)
{
	if (n == 1)
		return 1;
	if (n == 2)
		return 2;
	if (n > 2)
		return F(n - 1) + F(n - 2);
}

int climbStairs_F(int n) {
	return F(n);
}

int main()
{
	cout << climbStairs_F(10) << endl;
	system("pause");
	return 0;
}

3.分治
分治算法,根据字面意思解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

案例:二分查找,快速排序,归并排序

//二分查找代码
#include <iostream>
using namespace std;

int binarySearch(int array[], int n, int target)
{
	int low = 0, high = n, middle = 0;

	while (low < high)
	{
		middle = (low + high) / 2;
		if (target == array[middle])
		{
			return middle;
		}
		else if (target > array[middle])
		{
			low = middle + 1;
		}
		else if (target < array[middle])
		{
			high = middle;
		}
	}
	return -1;
}

int main()
{
	int array[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int r = binarySearch(array, 10, 3);
	cout << r << endl;

	system("pause");
	return 0;
}

二、排序算法

在这里插入图片描述

4.冒泡排序
冒泡排序是通过比较两个相邻元素的大小实现排序,如果前一个元素大于后一个元素,就交换这两个元素。这样就会让每一趟冒泡都能找到最大一个元素并放到最后。

冒泡排序代码

#include<iostream>
using namespace std;

//冒泡排序
int main()
{
	int a[9] = { 1,9,2,5,8,3,7,4,6 };
	int i, j;
	for (i = 0; i<9; i++)
	{
		for (j = 8; j>i; j--)
		{
			if (a[j - 1] > a[j])
			{
				int temp = a[j - 1];
				a[j - 1] = a[j];
				a[j] = temp;
			}
		}
	}
	for (int i = 0; i<9; i++)
		cout << a[i] << " ";
	system("pause");
	return 0;
}

5.选择排序
选择排序的思想是,依次从「无序列表」中找到一个最小的元素放到「有序列表」的最后面。以 arr = [ 8, 1, 4, 6, 2, 3, 5, 4 ] 为例,排序开始时把 arr 分为有序列表 A = [ ], 无序列表 B = [ 8, 1, 4, 6, 2, 3, 5, 4 ],依次从 B 中找出最小的元素放到 A 的最后面。这种排序也是逻辑上的分组,实际上不会创建 A 和 B,只是用下标来标记 A 和 B。

以 arr = [ 8, 1, 4, 6, 2, 3, 5, 4 ] 为例,第一次找到最小元素 1 与 8 进行交换,这时有列表 A = [1], 无序列表 B = [8, 4, 6, 2, 3, 5, 4];第二次从 B 中找到最小元素 2,与 B 中的第一个元素进行交换,交换后 A = [1,2],B = [4, 6, 8, 3, 5, 4];就这样不断缩短 B,扩大 A,最终达到有序。

选择排序代码

#include <iostream>
#include <algorithm>

using namespace std;

void selectionSort(int arr[], int n) {

	for (int i = 0; i < n; i++) {
		// 寻找[i, n)区间里的最小值
		int minIndex = i;
		for (int j = i + 1; j < n; j++)
			if (arr[j] < arr[minIndex])
				minIndex = j;

		swap(arr[i], arr[minIndex]);
	}

}

int main() {

	int a[10] = { 8,1,4,6,2,3,5,4 };
	selectionSort(a, 8);
	for (int i = 0; i < 8; i++)
		cout << a[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}

6.插入排序
在整个排序过程中,以 arr = [ 8, 1, 4, 6, 2, 3, 5, 7] 为例,它会把 arr 分成两组 A = [ 8 ] 和 B = [ 1, 4, 6, 2, 3, 5, 7] ,逐步遍历 B 中元素插入到 A 中,最终构成一个有序序列。

插入排序代码

//插入排序
#include <cstdio>
#include<iostream>
using namespace std;
void InsertSort(int a[], int n)
{
	for (int j = 1; j < n; j++)
	{
		int key = a[j]; //待排序第一个元素
		int i = j - 1;  //代表已经排过序的元素最后一个索引数
		while (i >= 0 && key < a[i])
		{
			//从后向前逐个比较已经排序过数组,如果比它小,则把后者用前者代替,
			//其实说白了就是数组逐个后移动一位,为找到合适的位置时候便于Key的插入
			a[i + 1] = a[i];
			i--;
		}
		a[i + 1] = key;//找到合适的位置了,赋值,在i索引的后面设置key值。
	}
}

int main() {
	int d[] = { 8, 1, 4, 6, 2, 3, 5, 7 };
	cout << "输入数组  { 8, 1, 4, 6, 2, 3, 5, 7 } " << endl;
	InsertSort(d, 8);
	cout << "排序后结果:";
	for (int i = 0; i < 8; i++)
	{
		cout << d[i] << " ";
	}

	cout << endl;
	system("pause");
	return 0; 

}

7.希尔排序
它的核心思想是把一个序列分组,对分组后的内容进行插入排序,这里的分组只是逻辑上的分组,不会重新开辟存储空间。它其实是插入排序的优化版,插入排序对基本有序的序列性能好,希尔排序利用这一特性把原序列分组,对每个分组进行排序,逐步完成排序。

希尔排序代码

//希尔排序

#include <iostream>
#include <stdlib.h>
using namespace std;

void ShellSort(int array[], int n)  //希尔排序函数
{
	int i, j, step;
	for (step = n / 2; step > 0; step = step / 2)  
	{
		for (i = 0; i < step; i++)  //i是子数组的编号
		{
			for (j = i + step; j < n; j = j + step)  //数组下标j,数组步长下标j+step
			{
				if (array[j] < array[j - step])
				{
					int temp = array[j];  //把数组下标j的值放到temp中
					int k = j - step;

					while (k >= 0 && temp < array[k])
					{
						array[k + step] = array[k];  //把大的值往后插入
						k = k - step;
					}
					array[k + step] = temp;  //把小的值往前插入
				}
			}
		}
	}
}


int main(void)  //主程序
{
	int array[] = { 8, 1, 4, 6, 2, 3, 5, 7 };

	ShellSort(array, 8);  // 调用BubbleSort函数  进行比较

	cout << "由小到大的顺序排列后:" << endl;
	for (int i = 0; i < 8; i++)
	{
		cout <<  array[i] << " ";
	}

	cout << endl << endl;  //换行

	system("pause");  //调试时,黑窗口不会闪退,一直保持
	return 0;
}

8.快速排序
快速排序的核心思想是对待排序序列通过一个「支点」(支点就是序列中的一个元素,别把它想的太高大上)进行拆分,使得左边的数据小于支点,右边的数据大于支点。然后把左边和右边再做一次递归,直到递归结束。

快速排序代码

#include <iostream>
#include <vector>

using namespace std;

//快速排序(从小到大)
void quickSort(int left, int right, int arr[])
{
	if (left >= right)
		return;
	int i, j, base, temp;
	i = left, j = right;
	base = arr[left];  //取最左边的数为基准数
	while (i < j)
	{
		while (arr[j] >= base && i < j)
			j--;
		while (arr[i] <= base && i < j)
			i++;
		if (i < j)
		{
			temp = arr[i];
			arr[i] = arr[j];
			arr[j] = temp;
		}
	}
	//基准数归位
	arr[left] = arr[i];
	arr[i] = base;
	quickSort(left, i - 1, arr);//递归左边
	quickSort(i + 1, right, arr);//递归右边
}

int main()
{
	int arr[8] = { 8, 1, 4, 6, 2, 3, 5, 7 };
	quickSort(0, 7, arr);

	for (int i = 0; i < 8; i++)
	{
		cout << arr[i] << " ";
	}

	cout << endl;
	system("pause");
	return 0;
}

9.归并排序
归并排序,采用分治思想,先把待排序序列拆分成一个个子序列,直到子序列只有一个元素,停止拆分,然后对每个子序列进行边排序边合并。其实,从名字「归并」可以看出一丝「拆、合」的意思。

归并排序代码

//c++归并排序
#include<iostream>

using namespace std;

void Merge(int arr[], int l, int q, int r) {
	int n = r - l + 1;//临时数组存合并后的有序序列
	int* tmp = new int[n];
	int i = 0;
	int left = l;
	int right = q + 1;
	while (left <= q && right <= r)
		tmp[i++] = arr[left] <= arr[right] ? arr[left++] : arr[right++];
	while (left <= q)
		tmp[i++] = arr[left++];
	while (right <= r)
		tmp[i++] = arr[right++];
	for (int j = 0; j<n; ++j)
		arr[l + j] = tmp[j];
	delete[] tmp;//删掉堆区的内存
}

void MergeSort(int arr[], int l, int r) {
	if (l == r)
		return;  //递归基是让数组中的每个数单独成为长度为1的区间
	int q = (l + r) / 2;
	MergeSort(arr, l, q);
	MergeSort(arr, q + 1, r);
	Merge(arr, l, q, r);

}

int main() {
	int a[8] = { 8, 1, 4, 6, 2, 3, 5, 7 };
	MergeSort(a, 0, 7);
	for (int i = 0; i<8; ++i)
		cout << a[i] << " ";

	cout << endl;
	system("pause");
	return 0;
}

10.计数排序
计数排序的核心思想是把一个无序序列 A 转换成另一个有序序列 B,从 B 中逐个“取出”所有元素,取出的元素即为有序序列。

计数排序代码

//计数排序
#include <iostream>
#include <vector>
using namespace std;

void CountSort(vector<int> &arr, int maxVal) {
	int len = arr.size();
	if (len < 1)
		return;
	vector<int> count(maxVal + 1, 0);
	vector<int> tmp(arr);
	for (auto x : arr)
		count[x]++;
	for (int i = 1; i <= maxVal; ++i)
		count[i] += count[i - 1];
	for (int i = len - 1; i >= 0; --i) {
		arr[count[tmp[i]] - 1] = tmp[i];
		count[tmp[i]]--;				//注意这里要减1
	}
}

int main()
{
	vector<int> arr = { 8, 1, 4, 6, 2, 3, 5, 4 };
	int maxVal = 8;
	CountSort(arr, maxVal);
	for (auto x : arr)
		cout << x << " ";
	cout << endl;

	system("pause");
	return 0;
}

11.桶排序
设置固定数量的空桶。
把数据放到对应的桶中。
对每个不为空的桶中数据进行排序。
拼接不为空的桶中数据,得到结果

桶排序代码


/*算法:桶排序*/

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void bucket_sort(int a[], int n, int max)
{
	int i, j;
	int *buckets;

	if (a == NULL || n<1 || max<1)
		return;

	// 创建一个容量为max的数组buckets,并且将buckets中的所有数据都初始化为0。
	if ((buckets = (int *)malloc(max * sizeof(int))) == NULL)
		return;
	memset(buckets, 0, max * sizeof(int));

	// 1. 计数
	for (i = 0; i < n; i++)
		buckets[a[i]]++;

	// 2. 排序
	for (i = 0, j = 0; i < max; i++)
		while ((buckets[i]--) >0)
			a[j++] = i;

	free(buckets);
}

int main() {
	int A[] = { 8, 1, 4, 6, 2, 3, 5, 7 };
	bucket_sort(A, 8, 9);
	for (int i = 0; i<8; i++)
		cout << A[i] << " ";
	
	cout << endl;
	system("pause");
	return 0;
}

12.基数排序
基数排序是从待排序序列找出可以作为排序的「关键字」,按照「关键字」进行多次排序,最终得到有序序列。

基数排序


#include <iostream>
using namespace std;

/*
* 打印数组
*/
void printArray(int array[], int length)
{
	for (int i = 0; i < length; ++i)
	{
		cout << array[i] << " ";
	}
	cout << endl;
}
/*
*求数据的最大位数,决定排序次数
*/
int maxbit(int data[], int n)
{
	int d = 1; //保存最大的位数
	int p = 10;
	for (int i = 0; i < n; ++i)
	{
		while (data[i] >= p)
		{
			p *= 10;
			++d;
		}
	}
	return d;
}
void radixsort(int data[], int n) //基数排序
{
	int d = maxbit(data, n);
	int tmp[10];
	int count[10]; //计数器
	int i, j, k;
	int radix = 1;
	for (i = 1; i <= d; i++) //进行d次排序
	{
		for (j = 0; j < 10; j++)
			count[j] = 0; //每次分配前清空计数器
		for (j = 0; j < n; j++)
		{
			k = (data[j] / radix) % 10; //统计每个桶中的记录数
			count[k]++;
		}
		for (j = 1; j < 10; j++)
			count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶
		for (j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中
		{
			k = (data[j] / radix) % 10;
			tmp[count[k] - 1] = data[j];
			count[k]--;
		}
		for (j = 0; j < n; j++) //将临时数组的内容复制到data中
			data[j] = tmp[j];
		radix = radix * 10;
	}
}

int main()
{
	int array[10] = { 73,22,93,43,55,14,28,65,39,81 };
	radixsort(array, 10);
	printArray(array, 10);
	system("pause");
	return 0;
}

三、搜索

1.二叉树的层次遍历

层次遍历很好理解,就是按二叉树从上到下,从左到右依次打印每个节点存储的数据。这不就是队列的拿手绝活么,先进先出。从左到右依次将每个数放入到队列中,然后按顺序依次打印就是想要的结果。
若根节点为空,直接返回; 若根节点非空,则将根节点入队,然后判断其左右子节点是否为空,若不为空,则压入队列。此时将根结点打印输出,并将根结点出队列。依次循环执行,直到队列为空。

二叉树的层次遍历代码

#include<iostream>
#include<queue>
using namespace std;
 
struct BinaryTreeNode{
	int value;
	BinaryTreeNode *m_pLeft;
	BinaryTreeNode *m_pRight;
};
 
void addBTNode(BinaryTreeNode **myBT,int val);//添加节点,满足每个父亲节点大于左边的,小于右边的 
void levels_showBT(BinaryTreeNode *myBT);//层次遍历,利用队列实现 
void levels_showBT_2(BinaryTreeNode *myBT);//层次遍历,利用队列实现 
 
int main(){
	
	BinaryTreeNode *myBT = nullptr;
	
	addBTNode(&myBT,10);
	addBTNode(&myBT,2);
	addBTNode(&myBT,3);
	addBTNode(&myBT,15);
	addBTNode(&myBT,18);
	addBTNode(&myBT,1);
	addBTNode(&myBT,16);
	
	levels_showBT(myBT);
	levels_showBT_2(myBT);
	
	return 0;
}
 
 
void addBTNode(BinaryTreeNode **myBT,int val){
	if(*myBT == nullptr){
		*myBT = new BinaryTreeNode();
		(*myBT)->value = val;
		(*myBT)->m_pLeft = nullptr;
		(*myBT)->m_pRight = nullptr;
		return;	
	}
		
	if(val == (*myBT)->value){
		return;
	}
	else if(val < (*myBT)->value){
		addBTNode(&(*myBT)->m_pLeft,val);
	}
	else{
		addBTNode(&(*myBT)->m_pRight,val);
	}			
}
 
void levels_showBT(BinaryTreeNode *myBT){//层次遍历,利用队列实现 
	if(myBT == nullptr )
		return;
	
	queue<BinaryTreeNode *> que;//构造一个树结点指针的队列
	que.push(myBT);
 
	while(!que.empty()){
		BinaryTreeNode *q = que.front();
		cout<<q->value<<" ";
		que.pop();
		
		if( q->m_pLeft != nullptr)//que.front()拿到最前结点 
		{
			que.push( q->m_pLeft );
		} 
			
			
		if( q->m_pRight != nullptr){
			que.push( q->m_pRight );
		} 
	}
	cout<<endl;		
}
 
void levels_showBT_2(BinaryTreeNode *myBT){//层次遍历,利用队列实现 
	if(myBT == nullptr )
		return;
	
	queue<BinaryTreeNode *> que;//构造一个树结点指针的队列
	que.push(myBT);
 
	while(!que.empty()){
		
		if( (que.front())->m_pLeft != nullptr)//que.front()拿到最前结点 
		{
			que.push( (que.front())->m_pLeft );
		} 		
			
		if( (que.front())->m_pRight != nullptr){
			que.push( (que.front())->m_pRight );
		} 
		
		cout<<(que.front())->value<<" ";
		que.pop();
	}
	cout<<endl;		
}

四、 线性表

数组

1.二维数组的查找
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

二维数组的查找代码

class Solution {
public:
 bool Find(int target, vector<vector<int> > array) {
     // 判断数组是否为空
     int m = array.size();
     if (m == 0) return false;
     int n = array[0].size();
     if (n == 0) return false;
     int r = 0, c = n-1; // 右上角元素
     while (r<m && c>=0) {
         if (target == array[r][c]) {
             return true;
         }
         else if (target > array[r][c]) {
             ++r;
         }
         else {
             --c;
         }
     }
     return false;
 }
};

数组拓展1
数组拓展2

链表

1.从链表的末尾添加节点
指针的指针参考
从链表的末尾添加节点参考

从链表的末尾添加节点代码

/*
 *  C++ 用指针的引用,好多了! 考虑得更简单。 
 */ 
#include <iostream>
#include <cstdlib>
#include <cstring>
#define BUG cout << "here\n";
#define STOP system("pause");
 
using namespace std;
 
struct node {
    int value;
    node* next;
    node() {
        value = 0;
        next = NULL;
    }
};
void addToTail(node* &phead, int value) {
    node* pn = new node();
    pn->value = value;
    if(phead == NULL) { // 考虑要全面。 
        phead = pn;
    }
    else {
        node* p = phead;
        while(p->next != NULL) {
            p = p->next;
        }
        p->next = pn;
    }
}
void addToTail(node** phead, int value) { // 之所以用指向指针的指针 -- 理解  复杂化的简单对比思想来理解 
    node* pn = (node*)malloc(sizeof(node));
    pn->next = NULL;
    pn->value = value;
    if(*phead == NULL) { // 考虑要全面。 
        *phead = pn;
    }
    else {
        node* p = *phead;
        while(p->next != NULL) {
            p = p->next;
        }
        p->next = pn;
    }
}    
int main() {
    node* head = NULL;
    addToTail(&head, 10);
    addToTail(&head, 20);
    node* p = head;
    while(p != NULL) {
        printf("%d->", p->value);
        p = p->next;
    }
    printf("NULL");
    STOP
    return 0;
}

2.从头到尾打印链表
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)
解题思路
3.反转链表
把一个单链表反转
解题思路
4.链表倒数第k个节点
输入一个链表,输出该链表中倒数第k个结点。
解题思路
5.合并两个排序的链表
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
解题思路
6.两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
解题思路

字符串

1.替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"
解题思路
2.字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列
解题思路

栈和队列

1.用两个栈实现队列
用两个栈实现一个队列
解题思路
2.从上往下打印二叉树
不分行从上往下打印出二叉树的每个节点,同层节点从左至右打印
解题思路

1.重建二叉树
给出先序和中序,重建二叉树,以层序输出
解题思路
2.树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构输入两棵二叉树A,B,判断B是不是A的子结构
解题思路
3.二叉树的后续遍历序列
给定一个二叉树,返回他的后序遍历的序列
解题思路
4.二叉树的深度
求二叉树的深度
解题思路

其他

设计LRU缓存结构
设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
解题思路

设计LRU缓存结构代码

class Solution {
public:
    /**
     * lfu design
     * @param operators int整型vector<vector<>> ops
     * @param k int整型 the k
     * @return int整型vector
     */
    // 存频率和频率对应节点链表的字典
    unordered_map<int, list<vector<int>>> freq_mp;
    // 存对应键值和节点
    unordered_map<int, list<vector<int>>::iterator> mp;
    // 记录最小频率 用来使用LFU策略找节点
    int min_freq=0;
    // 存剩余容量
    int size=0;
    
    vector<int> LFU(vector<vector<int>>& operators, int k){
        vector<int> res;
        size=k;
        for(int i=0;i<operators.size();++i){
            if(operators[i][0]==1){
                set(operators[i][1], operators[i][2]);
            }else{
                res.push_back(get(operators[i][1]));
            }
        }
        return res;
    }
    
    void update(list<vector<int>>::iterator iter, int key, int value){
        //取出双向链表中的一个节点 即 vector<int>
        int freq=(*iter)[0];
        freq_mp[freq].erase(iter);
        //如果该频率中已经没有节点
        //删除频率双向链表中的对应链表
        //查看是否需要更新最小频率
        if(freq_mp[freq].empty()){
            freq_mp.erase(freq);
            if(freq == min_freq){
                min_freq++;
            }
        }
        //向freq+1的双向链表头部中插入
        //由于原来的节点已经不存在 需要重新设置<key,value>的存储
        freq_mp[freq+1].push_front({freq+1, key, value});
        mp[key]=freq_mp[freq+1].begin();
    }
    
    void set(int key, int value){
        auto it=mp.find(key);
        if(it!=mp.end()){
            //链表中存在节点 更新value和频率
            update(it->second, key, value);
        }else{
            //哈希表中没有该节点
            //如果链表已满 删除频率最低而且最早的删掉
            //频率表中删除最后一个节点
            //删除对应<key,value>的节点
            if(size==0){
                int oldkey=freq_mp[min_freq].back()[1];
                freq_mp[min_freq].pop_back();
                if(freq_mp[min_freq].empty()){
                    freq_mp.erase(min_freq);
                }
                mp.erase(oldkey);
            }else{
                //容量未满 可以直接加入
                size--;
            }
            min_freq=1;
            freq_mp[1].push_front({1, key, value});
            mp[key]=freq_mp[1].begin();
        }
    }
    
    //get操作:没有找到则返回-1
    //找到则更新频率并且取出value返回
    int get(int key){
        int res=-1;
        auto it = mp.find(key);
        if(it==mp.end()){
            return res;
        }
        auto iter = it->second;
        res=(*iter)[2];
        update(iter, key, res);
        return res;
    }
};

参考:如何系统地学习数据结构与算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值