排序算法——希尔排序,归并排序,快速排序,基数排序

1.9.6 希尔排序

介绍
在这里插入图片描述

  • 实现大体分为交换法实现和插入法实现
  • 希尔排序是将原数组划分为多组,然后对每一组进行排序循环多次,最终让整个数组有序

交换法实现

  • 交换法咋一看好像是有点像冒泡,但是千万别将这个与冒泡弄混了。冒泡是在一次for循环内就确定一个极值,并将该极值放置到一极(最左或最右)经过arrays.length-1次循环最终将数组排序好。

  • 而交换法排序是三次for循环外层循环负责调整gap分组次数,中层for循环负责对原数组每个元素进行遍历,指针i指向当前遍历元素,最重要的内层循环每次对中层循环i指向的那个分数组,自index=0到index=i-gap下标的元素进行排序

    • 内层循环的一个例子
// 排序使用交换法 比如一组元素[3,5,1,6,2]
// 1. index = 0,3 与 5 比较[3,5,1,6,2]  currentIndex=0
// 2. index = 1,5 与 1 比较交换[3,1,5,6,2],currentIndex=1;再currentIndex-1回退进行3与1比较交换        		     [1,3,5,6,2] 直到currentIndex=0为止
// 3. index = 2,5 与 6 比较[1,3,5,6,2],回退3与5比较,再回退1与3比较 直到currentIndex=0
// 4. index = 3,6 与 2比较交换[1,3,5,2,6],回退比较交换[1,3,2,5,6].......直到[1,2,3,5,6] currentIndex=0为止
  • gap:初值为array.length/2 每次循环gap /= 2,直到gap <= 0为止
  • gap值既是每次分组的组数也是每一组组内元素之间的步长
import java.util.Arrays;

public class shellSort {
    public static void sort(int[] array){
        int temp;
        int count = 0;
        // 每轮排序完gap都会/2,gap既是一轮划分数组个数也是同组元素之间的步长
        for (int gap=array.length/2;gap > 0;gap /= 2){
            // 接下来的嵌套循环是为了遍历各组元素并对各组元素进行排序 保证该组元素有序
            for (int i=gap;i<array.length;i++){
                // 排序使用交换法 比如一组元素[3,5,1,6,2]
                // 1. index = 0,3 与 5 比较[3,5,1,6,2]  currentIndex=0
                // 2. index = 1,5 与 1 比较交换[3,1,5,6,2],currentIndex=1;再currentIndex-1回退进行3与1比较交换[1,3,5,6,2] 直到currentIndex=0为止
                // 3. index = 2,5 与 6 比较[1,3,5,6,2],回退3与5比较,再回退1与3比较 直到currentIndex=0
                // 4. index = 3,6 与 2比较交换[1,3,5,2,6],回退比较交换[1,3,2,5,6].......直到[1,2,3,5,6] currentIndex=0为止
                for(int j=i-gap;j >= 0;j -= 5){
                    if(array[j] > array[j+gap]){
                        temp = array[j];
                        array[j] = array[j+gap];
                        array[j+gap] = temp;
                    }
                }
            }
            System.out.println("希尔排序第"+(++count)+"趟排序: "+ Arrays.toString(array)+"  gap = "+gap);
        }
    }
    public static void main(String[] args) {
        int[] array = {8,9,1,7,2,3,5,4,6,0};
        sort(array);
    }
}

输出结果

array1: [8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
希尔排序第1趟排序: [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]  gap = 5
希尔排序第2趟排序: [0, 5, 1, 6, 3, 4, 7, 2, 9, 8]  gap = 2
希尔排序第3趟排序: [0, 1, 3, 4, 5, 6, 2, 7, 8, 9]  gap = 1

插入法实现

对分组的每一组的组内进行直接插入排序,得到有序数组

    public static void shellSort2(int[] array){
        int count = 0;
        for(int gap=array.length/2;gap>0;gap /= 2){
            for (int i=gap;i<array.length;i++){
                int j=i;
                int temp = array[j];
                if(array[j] < array[j-gap]){ // 只有当后一个元素小于前一个元素才有移位的意义
                    while (j-gap >=0 && temp < array[j-gap]){
                        // 将前一位元素后移一位
                        array[j] = array[j-gap];
                        j -= gap;
                    }
                    // 跳出循环后代表找到插入元素位置了 直接赋值插入
                    array[j] = temp;
                }
            }
            System.out.println("希尔排序第"+(++count)+"趟排序: "+ Arrays.toString(array)+"  gap = "+gap);
        }
    }

测试用例与输出结果

array2: [18, 19, 11, 17, 12, 13, 15, 14, 16, 10]
希尔排序第1趟排序: [13, 15, 11, 16, 10, 18, 19, 14, 17, 12]  gap = 5
希尔排序第2趟排序: [10, 12, 11, 14, 13, 15, 17, 16, 19, 18]  gap = 2
希尔排序第3趟排序: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]  gap = 1

1.9.7 快速排序

介绍
在这里插入图片描述
实现代码与输出结果

  • 个人认为难点在于当array[tempLeft/tempRight] == pivot时的指针移动在划分结束判断tempLeft == tempRight这两部分
import java.util.Arrays;
// 个人认为难点在于当array[tempLeft/tempRight] == pivot时的指针移动 和 在划分结束判断tempLeft == tempRight这两部分
public class quicklySortDemo {
    public  static void quicklySort(int[] array,int left,int right){
        int tempLeft = left; // 左指针
        int tempRight = right; //右指针
        int pivot = array[(left+right)/2]; // 中间值
        while (tempLeft < tempRight){
            while (array[tempLeft] < pivot)
                tempLeft++;
            while (array[tempRight] > pivot)
                tempRight--;
            // 判断是否划分结束,跳出循环
            if (tempLeft >= tempRight)
                break;
            // 交换异常数据
            int temp = array[tempLeft];
            array[tempLeft] = array[tempRight];
            array[tempRight] = temp;
            // 判断交换完数据是否为pivot 否则要进行指针移位 相等的话那指针就不会动了 这可以不行 事情还没做完呢
            if (array[tempLeft] == pivot)
                tempRight--;  // 右指针前移
            if (array[tempRight] == pivot)
                tempLeft++; // 左指针右移
        }
        System.out.println("划分: "+ Arrays.toString(array));
        // 需要先判断下l==r 否则stack会溢出(左递归和右递归有部分范围重合了)
        if(tempLeft == tempRight){
            tempRight--;
            tempLeft++;
        }
        // 跳出外层循环后 pivot原数组已划分好 开始对两侧数组进行递归划分
        if(left < tempRight){ // 左递归
            quicklySort(array,left,tempRight);
        }
        if (right > tempLeft) // 右递归
            quicklySort(array,tempLeft,right);
    }
    public static void main(String[] args) {
        int[] array2 = {18,19,11,17,12,13,15,14,16,10};
        quicklySort(array2,0,array2.length-1);
    }
}

输出结果

划分: [10, 11, 12, 17, 19, 13, 15, 14, 16, 18]
划分: [10, 11, 12, 17, 19, 13, 15, 14, 16, 18]
划分: [10, 11, 12, 14, 13, 15, 19, 17, 16, 18]
划分: [10, 11, 12, 13, 14, 15, 19, 17, 16, 18]
划分: [10, 11, 12, 13, 14, 15, 16, 17, 19, 18]
划分: [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

1.9.10 归并排序

介绍
在这里插入图片描述
在这里插入图片描述

  • 一次合并示例
    在这里插入图片描述
    思路

  • 由于需要反复拆分合并,初步选择采用递归来实现

  • 根据归并排序的思想,先要将原数组全部拆分后才能开始合并,所以应该写一个方法专门处理合并

  • 递归调用的是拆分方法,在拆分函数的最后调用合并merge方法(合并完一次刚好递归返回到上一层继续合并),这样可以实现从头到位拆分,再从尾到头合并

  • 如下图
    在这里插入图片描述

  • 在合并过程中使用一个额外数组temp来承载排序后的合并数组,然后将temp数组对应的元素拷贝到原数组内

  • 对数组的拆分与合并在实现上实际并不是真正的将元素拆分为其他数组然后再进行合并,这样对空间的浪费过于巨大,本质上是对指针的巧妙运用,只需要提供一个额外temp数组的空间就可以实现排序

实现代码与输出结果

import java.util.Arrays;

public class mergeSortDemo {
    private int[] array;
    public mergeSortDemo(int[] array){
        this.array  = array;
    }
    public void mergeSort(int left,int right,int[] temp){
        if(left < right){
            int mid = (left+right)/2; // 开始拆分数组
            // 开始左递归
            mergeSort(left,mid,temp);
            // 开始右递归
            mergeSort(mid+1,right,temp);
            // 递归结束 开始合并
            merge(left,mid,right,temp);
        }
        // 当递归到left >= right 会自动结束递归 然后开始返回
    }
    /**
     * 实现分解后数组的合并
     * @param left 指向数组的左指针
     * @param mid  指向数组的中间指针,指向右数组的前一位
     * @param right 指向右数组的最后一位
     * @param temp  临时数组,用于临时存放合后的数组
     */
    public void merge(int left,int mid,int right,int[] temp){
        int i = left;
        int j = mid+1;
        int t=0; // 指向temp数组下标的指针
        while (i <= mid && j <= right){
            if(array[i] <= array[j]){
                temp[t] = array[i];
                i++;
            }else {
                temp[t] = array[j];
                j++;
            }
            t++;
        }
        while (i <= mid){
            temp[t] = array[i];
            i++;
            t++;
        }
        while (j <= right){
            temp[t] = array[j];
            j++;
            t++;
        }
        // 开始拷贝数据到array
        int tempLeft = left;
        t = 0;
        System.out.print("合并结果: [ ");
        while (tempLeft <= right){
            array[tempLeft] = temp[t];
            System.out.print(array[tempLeft]+" ");
            t++;
            tempLeft++;
        }
        System.out.print("] \n");
    }
    public static void main(String[] args) {
        int[] array = new int[]{3,1,9,2,8,4,7,6,5,100,12,11};
        mergeSortDemo mergeSortDemo1 = new mergeSortDemo(array);
        mergeSortDemo1.mergeSort(0,array.length-1,new int[array.length]);
        System.out.println(Arrays.toString(array));
    }
}

输出结果

合并结果: [ 1 3 ] 
合并结果: [ 1 3 9 ] 
合并结果: [ 2 8 ] 
合并结果: [ 2 4 8 ] 
合并结果: [ 1 2 3 4 8 9 ] 
合并结果: [ 6 7 ] 
合并结果: [ 5 6 7 ] 
合并结果: [ 12 100 ] 
合并结果: [ 11 12 100 ] 
合并结果: [ 5 6 7 11 12 100 ] 
合并结果: [ 1 2 3 4 5 6 7 8 9 11 12 100 ] 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 100]

1.9.11 基数排序

介绍
在这里插入图片描述
思路
在这里插入图片描述

  • 下图是一轮排序的示例图
    在这里插入图片描述
  • 第二轮第三轮…第n轮同理,n为原数组中最大元素的位数

实现思路

  • 1.得到数组中位数最大数的位数n,n=基数排序需要排序的轮数
  • 2.定义一个二维数组buckets[10][array.length]每一行是一个一维数组对应下标从0-9的数字 将每一行理解为一个桶,桶内存放着对应元素
  • 3.定义一个一维数组bucketsCount[10] 用于存储二维数组中对应行数的桶内当前元素的个数
  • 4.开始多轮排序for(int i=0;i<n;i++)
    • 遍历原数组,获取原数组的每个元素的倒数第i位数字**(个,十,百,千,万…)**的的值, p = x / 10^i % 10
    • 根据数字值的不同将元素加入到对应的桶内
    • 原数组遍历结束,将在桶内元素按照桶的排序将元素重新加入到原数组内,实现一次排序
    • 每轮循环结束 都需要将bucketCount清空
  • 注意事项
    • 因为基数排序算法是牺牲空间来换取时间的。所以所以如果排序的数据过于巨大(比如上亿个数据之类的),将会导致java的JVM虚拟机报错内存不足。
    • 本代码实例的基数排序只能排序非负整数

实现代码与输出结果

import java.util.Arrays;

public class RadixSort {
    public static void radixSort(int[] array){
      // 1. 得到数组中最大元素的位数
        int max = array[0];
        int n = 0; // 数组最大元素的位数
        for (int value : array) {
            if (value > max)
                max = value;
        }
        while (max > 0){
            max /= 10;
            n++;
        }
//        System.out.println(n);
    // 2. 定义桶与记录桶内元素个数的一维数组
        int[][] buckets = new int[10][array.length];
        int[] bucketCounts = new int[10];
        int tempN;
        int index;
        for(int i=0;i < n;i++){
            // 获取原数组每个元素的倒数第i个数字
            for (int k : array) {
                tempN = (k / (int) Math.pow(10,i)) % 10;
                // 将该数字根据大小加入到对应的桶内 桶内元素计数也+1
                buckets[tempN][bucketCounts[tempN]] = k;
                bucketCounts[tempN]++;
            }
            index = 0;
            // 加入元素结束 再将所有元素按照桶的顺序加入到原数组内
            for (int j=0;j<bucketCounts.length;j++){
                for(int p=0;p<bucketCounts[j];p++){
                    array[index] = buckets[j][p];
                    index++;
                }
            }
            // 每轮循环结束 都需要将bucketCount清空
            for(int p=0;p<10;p++)
                 bucketCounts[p] = 0;
            System.out.println("第"+i+"轮排序: "+ Arrays.toString(array));
        }

    }
    public static void main(String[] args) {
        int[] array = new int[]{9,1,30,23,456,8546,154,1111,1223};
        radixSort(array);
    }
}

输出结果

0轮排序: [30, 1, 1111, 23, 1223, 154, 456, 8546, 9]1轮排序: [1, 9, 1111, 23, 1223, 30, 8546, 154, 456]2轮排序: [1, 9, 23, 30, 1111, 154, 1223, 456, 8546]3轮排序: [1, 9, 23, 30, 154, 456, 1111, 1223, 8546]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include #include #define MAXSIZE 10 #define MAX_BIT 8 // 关键字最大位数 #define RADIX 10 // 关键字基数 ,此时是十进制整数的基数 #define MAX_SPACE 8 // 分配的存储空间的大小 typedef char KeyType;// define the keyType is the int typedef int InfoType; typedef struct { KeyType key; InfoType otherinfo;// 其他数据项 }RedType; typedef struct { RedType r[MAXSIZE+1];// r[0] is for the guard int length; }SqList; // Radix Sorting struct SLNode { KeyType key[MAX_BIT]; // the key InfoType otheritems; // 其他数据项 int next; //下一个节点的下标位置 }; // 静态链表中的节点类型 struct SList { struct SLNode r[MAX_SPACE]; // 静态链表中各节点,其中r[0]为头结点 int keybit; // 关键字的位数 int recnum; // 静态链表中记录的个数 }; // 静态链表的类型 // 函数声明 void Insert_Sort ( SqList &L);// the Straight Insertion Sort void BInsert_Sort(SqList &L);// the Binary Insertion Sort void Shell_Sort(SqList &L);// Shell Sort int Partition ( SqList &L,int low,int high ); void Quick_Sort(SqList &L,int low,int high);// Quick Sort void Bubble_Sort(SqList &L); void Select_Sort( SqList &L); void Heap_Sort(SqList &L); void Merge_Sort(SqList &L);// Merging Sort void display(SqList L);// 输出显示 void main() { SqList L; L.length=MAXSIZE; int i,msg; char ch; do { system("cls"); // 清屏 cout<<"please input the data:"<<endl; for (i=1;i>L.r[i].key; } cout<<"1、直接插入排序"<<"\t"; cout<<"2、折半插入排序"<<endl; cout<<"3、希尔排序"<<"\t"; cout<<"4、冒泡排序"<<endl; cout<<"5、快速排序"<<"\t"; cout<<"6、直接选择排序"<<endl; cout<<"7、堆排序"<<"\t"; cout<<"8、归并排序"<<endl; cout<>msg; switch (msg) { case 1: Insert_Sort(L); display(L); break; case 2: BInsert_Sort(L); display(L); break; case 3: Shell_Sort(L); display(L); break; case 4: Bubble_Sort(L); display(L); break; case 5: Quick_Sort(L,1,L.length); display(L); break; case 6: Select_Sort(L); display(L); break; case 7: Heap_Sort(L); display(L); break; case 8: Merge_Sort(L); display(L); break; default: cout<<"please input 1至 8"<>msg; } cout<>ch; } while(ch=='Y'||ch=='y'); } // 直接插入排序 void Insert_Sort(SqList &L) { int i,j; for (i=2; i<=L.length; i++) { if (L.r[i].key<L.r[i-1].key)// if '<' insert the sort order { L.r[0] = L.r[i]; // see as a guard L.r[i]=L.r[i-1]; for ( j=i-2; L.r[0].key<L.r[j].key; j--)// try to find the right position { L.r[j+1]=L.r[j];// back } L.r[j+1]=L.r[0]; } } } //折半插入排序 void BInsert_Sort(SqList &L) { int i,j,low,high; for (i=2;i<=L.length; ++i) { L.r[0] = L.r[i];// access the SqList for the time being low=1; high=i-1; while ( low<=high ) { int m=( low+high )/2; if (L.r[0].key =high+1; --j) { L.r[j+1]=L.r[j]; } L.r[high+1] = L.r[0]; // insert it } } // 希尔排序 void Shell_Sort(SqList &L)// the ascending order { int d = L.length,i; while ( d>=1 ) { d=d/2; for (i = 1+d; i<=L.length; i++) if (L.r[i].key 0&&L.r[0].key <L.r[j].key; j-=d) { L.r[j+d]=L.r[j];// 将r[j]之前所有大于它的元素都后移 } L.r[j+d]=L.r[0];// 将r[i] 插入到正确位置 } } } // quick sort 快速排序 void Quick_Sort(SqList &L,int low,int high) { int pivotloc; if (low<high) { pivotloc=Partition(L,low,high);// One order divides into two Quick_Sort(L,low,pivotloc-1); Quick_Sort(L,pivotloc+1,high); } } int Partition ( SqList &L,int low,int high ) { // 对L.r[low]——L.r[high] 子序列进行一趟快速排序,返回分界线位置,即枢轴 L.r[0]=L.r[low]; int pivotkey=L.r[0].key; while (low<high ) { while (low=pivotkey) { high--; } L.r[low]=L.r[high];// while(False) while (low<high && L.r[low].key<=pivotkey) { low++; } L.r[high]=L.r[low];// while (false ) } L.r[low] =L.r[0]; return low; } void Bubble_Sort(SqList &L)// it's coming up 冒泡排序(上浮) { int i,j; int flag=1; // to flag the record whethe needs to exchange ,1 is need. i=1; while ( i<=L.length && flag==1) { flag=0; for (j=1; jL.r[j+1].key) { L.r[0]=L.r[j+1]; L.r[j+1]=L.r[j]; L.r[j]=L.r[0]; flag=1; } } i++; } } void Select_Sort( SqList &L)// 直接选择排序 { int i,j,min; for (i=1; i<= L.length; i++) { min=i; for (j=i+1; jL.r[j].key) { min=j; } } if (min!=i) { L.r[0]=L.r[i]; L.r[i]=L.r[min]; L.r[min]=L.r[0]; } } } void Heap_Adjust(SqList &L,int s, int m)// 堆调整 { // 在L.r[s...m]中记录的关键字除L.r[s].key 之外均满足堆的定义,本函数调整L.r[s]的关键字,使L.r[s...m]成为一个大顶堆 int j; L.r[0]=L.r[s]; for ( j=2*s; j<=m; j*=2 ) { // 沿key较大的孩子节点向下筛选 if (j<m &&L.r[j].key=L.r[j].key) { break; } else// 将三者中的最大值放入到根节点的位置,并用s记录此位置 { L.r[s]=L.r[j]; s=j; } } L.r[s]=L.r[0]; } void Heap_Sort(SqList &L) // 堆排序 { int i; for (i=L.length/2; i>0; i--)// build the heap { Heap_Adjust(L,i,L.length); } for (i=L.length; i>1; i--) { L.r[0]=L.r[i];// 将 堆顶记录和当前未经排序子队列(1——i)中 的 最后一个记录 交换 L.r[i]=L.r[1]; L.r[1]=L.r[0]; Heap_Adjust(L,1,i-1);// 将(1——i-1)重新调整为一个大堆 } } void Merge( RedType S[], RedType T[], int i,int m,int n) { // 将有序的S前一部分和后一部分归并为有序的T int j,k; for (j=m+1,k=i;i<=m && j<=n; ++k)// 将S中记录由小到大并入到T中 { if (S[i].key<=S[j].key) { T[k]=S[i++]; } else T[k] = S[j++]; } while(i<=m)// 将剩余的S[i...m]复制到T中 T[k++]=S[i++]; while(j<=n)// 将剩余的S[j...n]复制到T T[k++]=S[j++]; } void MSort(RedType S[],RedType T1[],int s,int t ) { // 将S[s...t]归并为T[s...t]MAXSIZE+1 RedType T2[MAXSIZE+1]; int m; if ( s==t )// 只有一个记录 { T1[s] = S[s]; } else { m=(s+t)/2; // 将S 分为2份 MSort(S,T2,s,m);// 将S归并为有序的T2 MSort(S,T2,m+1,t); // 将S归并为有序的T2 Merge(T2, T1,s,m,t); // 将T1和T2归并到T1中 } } void Merge_Sort(SqList &L)// 归并排序 { // 对顺序表L作归并排序 MSort(L.r,L.r,1,L.length); } // for Radix Sorting // 基数排序中一趟分配的算法 void Distribute (SLNode *r,int i,int f[RADIX],int e[RADIX]) { // 静态链表中的r域中记录已按key[0]——key[i-1]有序 // 本算法按关键字的第i位建立RADIX个链队列,使在同一链队列中关键字的第i位的数值相等; // f、e均为长度为RADIX的指针数组,分别指向各链队列的第一条和最后一条记录 int j,p; for (j=0; j<RADIX; j++) { f[j]=e[j]=0;// 各链队列初始化为空 } for (p=r[0].next; p; p=r[p].next ) { // p总是指向静态链表中的当前节点 j=r[p].key[i]; if (!f[j]) { f[j]=p; } else r[e[j]].next=p; e[j]=p; // 根据r[p]的关键字的第i位的值,将其归入相应的链队列,并修改相应的f、e指针 } } // 基数排序中的一趟收集的算法 void collect ( SLNode *r,int i,int f[RADIX],int e[RADIX] ) { // 本算法按关键字的第i位自小到大地将指针f[0]——f[RADIX-1] 所指向的各 链队列依次 链接成一个静态链表 int j; for ( j=0; !f[j]; j++) { r[0].next=f[j];// r[0].next 指向第一个非空链队列中的第一个节点 } int temp=e[j]; while ( j<RADIX ) { for ( j++; j<RADIX-1 && !f[j]; j++) // 找下一个非空链队列 { if (f[j]) // 链接二个队列 { r[temp].next=f[j]; temp=e[j]; } } } r[temp].next=0; // 将最后一个节点的next域设置为空,完成一趟收集 } void Radix_Sort(SList &L)// 基数排序(链式) { // 定义静态链表L,对L进行基数排序(L.r[0] 为头结点) int i; int e[RADIX],f[RADIX]; for (i=0; i<L.recnum; i++) { L.r[i].next=i+1;// 定义各节点的next 域,使之成为一个前后相连的静态链表 } L.r[L.recnum].next=0;// 定义最后一个结点的next域,使整个静态链表有效终止 for (i=0; i<L.keybit; i++) { // 按最低位优先依次对各关键字进行分配和收集 Distribute( L.r, i, f, e);// 第i躺分配 collect(L.r, i, f, e); // 第i躺收集 } } void display(SqList L) { cout<<"after the sort:"<<endl; for (int i=1;i<=L.length; i++ ) { cout<<L.r[i].key<<"\t"; if (i%5==0 ) { cout<<"\n"; } } }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值