数据结构八大排序;快排、归并非递归 || 万字大总结

目录

前言:

插入排序

代码实现

时间,空间复杂度分析

希尔排序

代码实现

时间,空间复杂度分析

堆排序

代码实现

时间,空间复杂度分析

快速排序

挖坑法

        代码实现

Hoare法(左右指针法)

        代码实现

前后指针法

        代码实现

 时间,空间复杂度分析

快速排序优化

整体代码实现

快速排序非递归

代码实现

归并排序

代码实现

时间,空间复杂度分析

归并排序非递归

代码实现

计数排序

代码实现

时间,空间复杂度分析

冒泡排序

代码实现

冒泡排序优化

时间,空间复杂度分析

小结:


前言:

🎈排序的算法有很多,对于数据量和数据所存在的特点不同,排序算法的使用场景也会相应的改变,这样才会增加我们程序的效率。

插入排序

🧢插入排序的算法思想,类似于我们打扑克,揭牌时整牌时的行为。我们首先认为一个数据是有序的,当我们拿到第二个数据,和前面数据去比较(这里排升序),如果比它小,把前面的数据就往后面放,直到找到前面数据比它小的时候,就放在这个数据的后面。

🧢我们每拿到一个数据,它前面的数据都是有序的,因此我们采取这样的思路当遍历完数组后,整组数据全都会有序。

代码实现

import java.util.Arrays;
public class InsertSort {
    public static void insertSort(int[] arr) {
        for(int i = 1; i < arr.length; i++) {
            int tmp = arr[i];
            int j = i - 1;
            for(; j >= 0; j--) {
                if(tmp < arr[j]) {
                    arr[j + 1] = arr[j];
                }else {
                    //arr[j + 1] = tmp;
                    break;
                }
            }
            arr[j + 1] = tmp;
        }
    }
    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1,0};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

时间,空间复杂度分析

最坏情况:O(N^2)

😯当一组数据是逆序的时候,这样每次拿到一组数据都要一直往前比,放在最前面。

最好情况:O(N)

😯当一组数据是升序的时候,我们只需遍历一遍数组,不用挪动数据。

空间复杂度:O(1)

😯没有开辟临时的空间。

稳定性:稳定

注意:时间复杂度最坏达到O(N^2),但当数据有序的时候达到O(N),对比就可得到,当一组数据接近有序时(数据量不多),插入排序是非常快的。

希尔排序

 😊希尔排序是对插入排序的一个升级,由于插入排序是拿到数据后一个一个比较,最终才能确定好位置。如果一个特别小的数据在最后面,那么把他往前放,就需挪动很多的数据。

😊希尔排序采取分组的思想,看上图,我们把1和7,5和4,6和3,各分为一组。然后把每组的数据进行插入排序,这样特别小的数据如果在最后面,很快就会放在前面去。这叫做预排序,每次排完后数据都会接近有序(到数据有序时,可能会分很多次组)。最终gap会为1,这个时候经历了预排序,数据接近有序,插入排序就会非常快。

😊分组的函数现在有很多说法,我们采取gap = gap / 2。

代码实现

import java.util.Arrays;

public class ShellSort {
    private static void insertSort(int[] arr, int gap) {
        for(int i = gap; i < arr.length; i++) {
            int tmp = arr[i];
            int j = i - gap;
            for( ; j >= 0; j -= gap) {
                if(tmp < arr[j]) {
                    arr[j + gap] = arr[j];
                }else {
                    //arr[j + 1] = tmp;
                    break;
                }
            }
            arr[j + gap] = tmp;
        }
    }
    public static void shellSort(int[] arr) {
        int gap = arr.length;
        while(gap > 0) {
            gap /= 2;
            insertSort(arr, gap);

        }
    }
    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1,0};
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

时间,空间复杂度分析

时间复杂度:O(N^1.3)

😃我们每次按照最坏的情况算,会算出一个值,但是每次预排序数据都会接近有序,这样算显然不对。直到现在也没有算出一个确切的值,我们就先记住这个值。

空间复杂度:O(1)

😃没有额外开辟空间

稳定性:不稳定

堆排序

🎉堆排序的前提是我们首先要将这组数据建堆,建堆以及堆的概念,对于堆的一些操作在下面这篇博客中有详细介绍。

(141条消息) 模拟实现优先级队列_小小太空人w的博客-CSDN博客https://blog.csdn.net/weixin_62353436/article/details/127301135🪖我们要排升序,就要建大堆。大堆最大的数据在一组数据的最前面,我们将这个数据换到最后面。由于数据原本就是大堆的结构,换完之后,只需要将第一个数据向下调整(建大堆),由于最后一个数据位置已经确定,这次向下调整,就不用包含最后一个数据,这样就会又确定一个最大的数据在最前面。依次循环下去,我们就逐渐确定好每一个数据的位置,整组数据也就有序了。

代码实现

import java.util.Arrays;
public class HeapSort {
    //向下调整时间复杂度:log(n)
    private static void shiftDown(int[] arr, int parent, int len) {
        int child = (parent * 2) + 1;
        while(child < len) {
            if(child + 1 < len && arr[child] < arr[child + 1]) {
                child++;
            }
            if(arr[parent] < arr[child]) {
                int tmp = arr[parent];
                arr[parent] = arr[child];
                arr[child] = tmp;
                parent = child;
                child = parent * 2 + 1;
            }else {
                break;
            }
        }
    }
    /**
     * 时间复杂度:
     * O(n) + O(n*logn) 约等于 O(nlogn)
     * 空间复杂度:O(1)
     * @param arr
     */
    public static void heapSort(int[] arr) {
        //建堆
        //时间复杂度:O(n) 错位相减法推导
        for(int i = (arr.length - 1 - 1) / 2; i >= 0; i--) {
            shiftDown(arr, i, arr.length);
        }
        //交换元素,建堆
        //时间复杂度:n * log(n)
        int end = arr.length - 1;
        while(end > 0) {
            int tmp = arr[end];
            arr[end] = arr[0];
            arr[0] = tmp;
            shiftDown(arr, 0, end);
            end--;
        }
    }
    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

时间,空间复杂度分析

时间复杂度:O(N * log(N))

🎄向下调整,调整的是数的高度log(n),我们从那个父亲节点(上面那个博客有介绍),向前依次向下调整(建堆)。堆的结构是一棵树,我们可以计算出每个节点向下调整的次数,然后求和,根据错位相减法,可求得建堆的时间复杂度为O(N)。然后将每个最大的数据依次放在最后面,向下调整,时间复杂度为:O(N * log(N))。总的时间复杂度为:O(N * log(N)) + O(N)约等于O(N * log(N))。

空间复杂度:O(1)

🎄没有额外开辟空间。

稳定性:不稳定

快速排序

挖坑法(图以右边做为key)

😆首先把最左面的数据取出来作为key,这时候坑就在最左面。然后从数据的右面开始找比key小的数据,放入坑位。这个时候坑位就在右面那个比key小的数据那里。然后从左面找比key大的数据,放入新的坑位。依次循环,直到左右指针相遇,key放入这里。这样走一遍就可以保证key的左面数据比它小,右面比它大,即这个数据位置就确定了,这个位置称为基准。

😆然后分别递归这个数据的左右区间,当递归到剩余一个数据时,认为有序。递归时每次都可以确定一个数据的位置,当左右区间递归完成后,整组数据就有序了。

代码实现

🤨这里会讲解三种方法找基准,它们递归的思路都是一致的。这里先展示找基准的代码,整体代码在后面展示

 private static int partition(int[] arr, int left, int right) {
        int pivot = arr[left];
        while(left < right) {
            while(left < right && arr[right] >= pivot) {
                right--;
            }
            arr[left] = arr[right];
            while(left < right && arr[left] <= pivot) {
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = pivot;
        return left;

    }

Hoare法(左右指针法)

💎找最左面作为key,然后从最右面开始找比key小的数据,从左面找比key大的数据。找到后两个数据交换。直到左右指针相遇,交换最左面和相遇位置的数据。最终,key的左面比它小,右面比它大,基准就可以确定。

注意:如果认为最左面数据为key,则必须先从右边开始找比它小的数据,这样才能保证最终key的左面比它小,右面比它大。右面和左面找小和大的数据时,必须取等号,防止左右两边数据一样时,会死循环。

代码实现

 private static int partitionHoare(int[] arr, int left, int right) {
        int tmp = left;
        int pivot = arr[left];
        while(left < right) {
            //必须取等号,防止左右两端数据一致,死循环
            //左面取key,必须右面先走,才能保证左面比key小,右面比key大
            while(left < right && arr[right] >= pivot) {
                right--;
            }
            while(left < right && arr[left] <= pivot) {
                left++;
            }
            swap(arr, right, left);
        }
        swap(arr, left, tmp);
        return left;
    }

前后指针法

😉定义prev在起始位置,cur在其后面的位置,最左面的数据作为key。cur往前走找比key小的数据,找到后,prev往前走,交换两个位置的数据。如果cur找到比key小的数据时,prev往前走,两个指针指向同一个数据,就没有必要交换了。当cur遍历完数组后,把key放在prev的位置。这时候key的左面比它小,右面比它大,就可以确定基准。

代码实现

 private static int partition2(int[] arr, int left, int right) {
        int prev = left;
        int cur = left + 1;
        while(cur <= right) {
            if(arr[cur] < arr[left] && arr[++prev] != arr[cur]) {
                swap(arr, prev, cur);
            }
            cur++;
        }
        swap(arr, prev, left);
        return prev;
    }

时间,空间复杂度分析

时间复杂度:O(N * log(N))

😯递归就是一棵树,它每层都有N个数据,有log(N层)。

空间复杂度:O(log(N))

😯递归是要为函数开辟栈帧的,有log(N)层。

稳定性:不稳定

快速排序优化

🎈如果一组数据是逆序的,将最左面数据作为key。从右面找比它小的数据,左面没有数据,确定基准后左面也没有数据。这样时间复杂度会很大,就是一个等差数列,达到O(N^2),空间复杂度也会达到O(N)(类似一个单链表)。

🎈我们可以换一个key,找完基准后,保证它的左右都有数据。采取三位取中法,最左面和中间和最右面找中间大的数据,然后将他换到最左面。这样就算是逆序的数据,找完基准后,可以保证它的左右区间都有数据。

🎈快速排序,采用的是递归的思想。递归就怕递归太深了,导致栈溢出(StackOverFlow)。我们可以想到递归太深,大量数据都是在下面,而且它们也都是接近有序的,这个时候采取直接插入排序就会快很多,也可以防止继续向下递归。

整体代码实现

import java.util.Arrays;

public class QuickSort {
    private static void insertSort(int[] arr, int left, int right) {
        for(int i = left + 1; i <= right; i++) {
            int tmp = arr[i];
            int j = i - 1;
            for( ; j >= left; j--) {
                if(tmp < arr[j]) {
                    arr[j + 1] = arr[j];
                }else {
                    //arr[j + 1] = tmp;
                    break;
                }
            }
            arr[j + 1] = tmp;
        }
    }
    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    /**
     * Hoare法,左右指针法
     * 快速排序:找基准 确定一个数的位置,左边比它小,右边比它大
     * 时间复杂度:O(n*logn)  每层都是n个数据,有log(n)层
     * 空间复杂度:O(logn) 递归树的高度
     * @param arr
     * @param left
     * @param right
     * @return
     */
    private static int partitionHoare(int[] arr, int left, int right) {
        int tmp = left;
        int pivot = arr[left];
        while(left < right) {
            //必须取等号,防止左右两端数据一致,死循环
            //左面取key,必须右面先走,才能保证左面比key小,右面比key大
            while(left < right && arr[right] >= pivot) {
                right--;
            }
            while(left < right && arr[left] <= pivot) {
                left++;
            }
            swap(arr, right, left);
        }
        swap(arr, left, tmp);
        return left;
    }
    /**
     * 挖坑法
     * 先将左面作为key存起来,即坑位。右面找小的数据,往坑位放,即右面为坑位,左面找大的数据往坑位放
     * @param arr
     * @param left
     * @param right
     * @return
     */
    private static int partition(int[] arr, int left, int right) {
        int pivot = arr[left];
        while(left < right) {
            while(left < right && arr[right] >= pivot) {
                right--;
            }
            arr[left] = arr[right];
            while(left < right && arr[left] <= pivot) {
                left++;
            }
            arr[right] = arr[left];
        }
        arr[left] = pivot;
        return left;

    }
    /**
     * 前后指针法
     * cur找比key小的数据,往后面放,当prev和cur在同一位置时,就不用放了,最后将prev和key换
     * @param arr
     * @return
     */
    private static int partition2(int[] arr, int left, int right) {
        int prev = left;
        int cur = left + 1;
        while(cur <= right) {
            if(arr[cur] < arr[left] && arr[++prev] != arr[cur]) {
                swap(arr, prev, cur);
            }
            cur++;

        }
        swap(arr, prev, left);
        return prev;
    }
    private static int findMidValOfIndex(int[] arr, int begin, int end) {
        int mid = (begin + end) >> 1;
        if(arr[begin] < arr[end]) {
            if(arr[mid] > arr[end]) {
                return end;
            }else if(arr[mid] < arr[begin]) {
                return begin;
            }else {
                return mid;
            }
        }else {
            if(arr[mid] > arr[begin]) {
                return begin;
            }else if(arr[mid] < arr[end]){
                return end;
            }
            return mid;
        }
    }
    private static void _quickSort(int[] arr, int begin, int end) {
        //剩余一个数,认为有序,递归结束条件
        if(begin >= end) {
            return;
        }
        //递归到后面数据量占总数的绝大多数,也接近有序,直接采用插入排序
        if(end - begin + 1 <= 15) {
            insertSort(arr, begin, end);
            return;
        }
        //三位取中,防止最坏情况(数据有序)没有左区间,
        //保证关键子在中间,左右都有区间
        //空间复杂度达到O(n)   时间复杂度达到O(n^2)
        int index = findMidValOfIndex(arr, begin, end);
        swap(arr, index, begin);

        int pivot = partition2(arr, begin, end);
        //分别去递归这个基准的左右区间
        _quickSort(arr, begin, pivot - 1);
        _quickSort(arr, pivot + 1, end);
    }
    public static void quickSort(int[] arr) {
        _quickSort(arr, 0, arr.length - 1);
    }
    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1,0};
        quickSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

快速排序非递归

🤞利用栈,用区间控制模拟递归的一个过程。首先将一组数据的左右区间入栈,然后栈不为空的话,弹出这个区间,去找基准。找到后分别将这个基准的左右区间入栈,然后再去判断栈不为空,这个时候弹出的区间就是这个基准的右区间,依次循环下去,直到栈为空。如果区间剩余一个数据,就不用入栈了,认为它是有序的。

代码实现

public static void quickSort(int[] arr) {
        Stack<Integer> stack = new Stack<>();
        int begin = 0;
        int end = arr.length - 1;
        if(end > begin) {
            stack.push(begin);
            stack.push(end);
        }
        while(!stack.isEmpty()) {
            end = stack.pop();
            begin = stack.pop();
            int pivot = findPivot(arr, begin, end);
            if(pivot - 1 > begin) {
                stack.push(begin);
                stack.push(pivot - 1);
            }
            if(end > pivot + 1) {
                stack.push(pivot + 1);
                stack.push(end);
            }
        }
    }

归并排序

😃归并思想,如果两组数据,都是有序的。开辟一个可以存储两组数据的空间,然后同时遍历这两组数据,进行比较。把小的数据往开辟的数组里放。然后遍历这个数组的指针往后走,继续比较,也是放小的数据。最终肯定有一个数组先遍历完成,然后将没有遍历完数组中的数据拷贝到新开辟的数组内。

😃那么一组数据的左右区间无序怎么办?采取递归的思想,当递归到剩余一个数据时认为数据是有序的,然后进行归并。最终会是一个一个,两个两个,四个四个等等有序的,当递归回退到整组数据的区间时,归并后,整组数据就会有序了。

😃最后需将有序的这个数组拷贝到原来的数组,由于是分区间递归下去的,那么当归并到一组数据的右区间有序时,也要将这组有序数据拷贝到这个右区间。

代码实现

import java.util.Arrays;

public class MargeSort {
    private static void marge(int[] arr, int begin, int mid, int end) {
        int len = end - begin + 1;
        int[] tmp = new int[len];
        int index = 0;
        int begin1 = begin;
        int end1 = mid;
        int begin2 = mid + 1;
        int end2 = end;
        while(begin1 <= end1 && begin2 <= end2) {
            if(arr[begin1] <= arr[begin2]) {
                tmp[index++] = arr[begin1++];
            }else {
                tmp[index++] = arr[begin2++];
            }
        }
        while(begin1 <= end1) {
            tmp[index++] = arr[begin1++];
        }
        while(begin2 <= end2) {
            tmp[index++] = arr[begin2++];
        }
        for(int i = 0; i < index; i++) {
            arr[i + begin] = tmp[i];
        }
    }
    private static void margeSortChild(int[] arr, int begin, int end) {
        if(begin == end) {
            return;
        }
        int mid = (begin + end) >> 1;
        //递归左右区间,到一个数据,认为有序
        margeSortChild(arr, begin, mid);
        margeSortChild(arr, mid + 1, end);
        //合并
        marge(arr, begin, mid, end);
    }
    public static void margeSort(int[] arr) {
        margeSortChild(arr, 0, arr.length - 1);
    }
    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1,0};
        margeSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

时间,空间复杂度分析

 时间复杂度:O(N * log(N))

🪖递归的思想,每层都有N个数据,有log(N)层。

空间复杂度:O(N)

🪖最终会开辟一个有N个数据的数组。

稳定性:稳定

归并排序非递归

😄我们利用gap控制区间,实现每组数据的归并。首先将gap设置为1,一个,一个数据归并。gap以二倍的速度增长,如果有10个数据,那么最后一次归并gap就是8,即gap需小于数组的长度。在进行归并的时候,每次归并的是两组数据。归并完成后,遍历数组的下标需向后跳两倍的gap。

😄当我们拿到一组数据的区间,和中间位置就可以归并,如果有10个数据,当gap为8时,确定右区间时end就有可能越界,这时就需修正它到数组的最后一个位置。如果有1个数据,确定中间位置也会越界,也需修正它到数组的最后一个位置。

代码实现

import java.util.Arrays;

public class MargeSort2 {
    private static void marge(int[] arr, int left, int mid, int right) {
        int begin1 = left;
        int end1 = mid;
        int begin2 = mid + 1;
        int end2 = right;
        int[] tmp = new int[right - left + 1];
        int index = 0;
        while(begin1 <= end1 && begin2 <= end2) {
            if(arr[begin1] <= arr[begin2]) {
                tmp[index++] = arr[begin1++];
            }else {
                tmp[index++] = arr[begin2++];
            }
        }
        while(begin1 <= end1) {
            tmp[index++] = arr[begin1++];
        }
        while(begin2 <= end2) {
            tmp[index++] = arr[begin2++];
        }
        for(int i = 0; i < index; i++) {
            arr[i + left] = tmp[i];
        }
    }
    /**
     * 一个一个有序,两个两个有序,四个四个有序
     * @param arr
     */
    public static void margeSort(int[] arr) {
        int gap = 1;
        while(gap < arr.length) {
            //一组内有两个gap
            for(int i = 0; i < arr.length; i += gap * 2) {
                int left = i;
                int mid = left + gap - 1;
                int right = mid + gap;
                //可能越界,修正到最后面
                if(mid > arr.length - 1) {
                    mid = arr.length - 1;
                }
                if(right > arr.length - 1) {
                    right = arr.length - 1;
                }
                marge(arr, left, mid, right);
            }
            gap *= 2;
        }
    }
    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1,0};
        margeSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

计数排序

🙂找出一组数据中最小和最大的数据,然后开辟一个这个区间大小的数组。遍历数组,把每个数据减去最小值,作为下标,让计数数组加1。当遍历完成后,就可以统计出每个数据的个数。再去遍历开辟的这个数组,当计数数组不为0时,用这个下标加上最小值,依次存储到原来的数组。计数数组是几就存储几次,这样最终整组数据都会有序。

代码实现

import java.util.Arrays;

public class CountSort {
    public static void countSort(int[] arr) {
        int index = 0;
        int min = arr[0];
        int max = arr[0];
        for(int i = 1; i < arr.length; i++) {
            if(arr[i] > max) {
                max = arr[i];
            }
            if(arr[i] < min) {
                min = arr[i];
            }
        }
        int[] tmp = new int[max - min + 1];
        for(int i = 0; i < arr.length; i++) {
            tmp[arr[i] - min]++;
        }
        //O(n + 范围)
        for(int i = 0; i < tmp.length; i++) {
            while(tmp[i] != 0) {
                arr[index++] = i + min;
                tmp[i]--;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1,0};
        countSort(arr);
        System.out.println(Arrays.toString(arr));
    }

}

时间,空间复杂度分析

时间复杂度:O(N + 范围)

😐第一次遍历数组O(N),第二次遍历数组O(N),第三次遍历开辟的数组O(n + 范围),计数排序适合对范围性数据排序。

空间复杂度:O(范围)

😐开辟了一个范围大小的数组。

稳定性:不稳定

冒泡排序

😣第一个和第二个数据比较,大的数据往后交换,然后两两比较,当遍历完数组后,最大的数据就会冒到最后面。再去冒0 -  n-1这个区间的数据,把最大的数据冒到n-1这个位置。依次循环,每冒一个数据,就确定一个数据,也少比较一个数据。

代码实现

import java.util.Arrays;

public class BubbleSort {
    public static void bubbleSort(int[] arr) {
        for(int i = 0; i < arr.length - 1; i++) {
            //优化操作,数据本来就有序
            boolean flag = false;
            for(int j = 0; j < arr.length - 1 - i; j++) {
                if(arr[j] > arr[j + 1]) {
                    int tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                    flag = true;
                }
                if(!flag) {
                    break;
                }
            }
        }
    }
    public static void main(String[] args) {
        int[] arr = {9,8,7,6,5,4,3,2,1,0};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

冒泡排序优化

😯如果第一次遍历完数组后,没有挪动数据,说明数组就是有序的,直接break跳出循环。

时间,空间复杂度分析

时间复杂度:O(N^2)

🤞每次冒一个数据到后面,这就是一个等差数列,时间复杂度为O(N^2)。考虑优化的最好时间复杂度:O(N),只遍历一遍数组。

空间复杂度:O(1)

🤞没有开辟新的数组。

稳定性:稳定

小结:

🐵排序算法代码的实现,有很多细节性的东西,我们要理解算法的思路,考虑全面,然后再去写代码。

  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小小太空人w

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

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

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

打赏作者

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

抵扣说明:

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

余额充值