排序算法笔记详解(上)

1、冒泡排序

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-11 11:21
 * @Des 冒泡排序-左程云算法学习笔记
 */
public class BubbleSort {
    public static void bubbleSort(int[] arr){
        if(arr == null || arr.length < 2){
            return ;
        }
        for(int e=arr.length-1;e>0;e--){ // 0 ~ e范围上
            for(int i=0;i<e;i++){
                if(arr[i] > arr[i+1]){ //因为上边是e-1,所有此处i+1不用担心溢出的问题。
                    swap(arr,i,i+1);
                }
            }
        }
    }
    /**
     * 异或运算还可以理解为无进位相加
     * 性质:
     *  0^N = N; N^N=0;
     *  满足交换律和结合律 a^b = b^a  (a^b)^c = a^(b^c)
     *  推出性质3
     *  a=甲 b=乙:
     *  a = a ^ b
     *  b = a ^ b
     *  a = a ^ b
     *  好处:少了一个空间的占用
     *  从第二行,用第一行结论: b= (a^b)^b ==> a^0=a  所以b就变为了a;
     *  从第三行,用第一行,第二行结论: a= (a^b) ^ a ==> b^0=b  所以a就变成了b;
     *  前提:a和b在内存中是一块独立的区域,他俩的值可以一样,但必须内存不一样。
     */
    //异或运算:相同为0,不同为1;
    public static void swap(int[] arr,int i,int j){
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }
    /**
     * 面试题整理2
     */
    public static void printOddTime2(int[] arr){
        int eor = 0;
        for(int i=0;i<arr.length;i++){
            eor^=arr[i];
        }
        int rightOne = eor & (~eor +1); //提出最右边的1
        int onlyOne = 0;
        for(int cur: arr){
            if((cur & rightOne) == 0){  //if((cur & rightOne) == 1){ //也行
                onlyOne ^= cur;
            }
        }
        System.out.println(onlyOne+" "+(eor^onlyOne));
    }

}
//面试题【在一个整型数组中,1个数字出现了奇数次,其他所有数字出现了偶数次,怎么找到出现了奇数次的数】从左到右异或即可。o(n) o(1)
//面试题【在一个整型数组中,2个数字出现了奇数次,其他所有数字出现了偶数次,怎么找到出现了奇数次的数】
//  eor=a^b 肯定!=0(假设第八位是1,a和b的第八位肯定不同,一个为1,一个为0)
//  (数字1是第八位为1异或的最终结果)eor'=a or b
//  (数字2)eor^eor' ====> a^b ^(a or b)

/**
 * 冒泡排序算法是稳定的算法:每次比较,每次交换,大数后移,每一轮都能找到一个最大的数字放到最后;
 */
/**
 * 冒泡排序与选择排序最大的不同:冒泡排序是每次比较后交换;选择排序是一轮找到最小值下标后交换。
 */

2、选择排序

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-11 11:20
 * @Des 选择排序-左程云算法学习笔记
 */
public class SelectSort {
    public static void selectSort(int[] arr){
        if(arr == null || arr.length < 2){
            return;
        }
        for(int i=0;i<arr.length;i++){  // i~N-1
            int minIndex = i;
            for(int j=i+1;j<arr.length;j++){    //i ~ N-1上找到最小值的下标
                minIndex = arr[minIndex] < arr[j] ? minIndex : j;
            }
            swap(arr,i,minIndex); //找到了最小的数字之后,就根据下标交换数值
        }
    }
    public static void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

/**
 * 疑问:该排序稳定吗?怎么判断的
 * minIndex = arr[minIndex] < arr[j] ? minIndex : j; 该种判断不稳定,如果相等了,minIndex就与j交换了位置;
 * minIndex = arr[minIndex] < = arr[j] ? minIndex : j; 那么此种方式呢?稳定吗?也不稳定;
 * 6、7、6、2、8,在对其进行第一遍循环的时候,会将第一个位置的6与后面的2进行交换。此时,就已经将两个6的相对前后位置改变了。因此选择排序不是稳定性排序算法。
 * 6、7、6、2、8,在对其进行第一遍循环的时候,会将第一个位置的6与第二个位置的6交换的话,这是不可能的;
 * 选择排序算法的核心是:每次排序都找出最小的数,让第一个数【最前边的数字】与其做交换,让最小的数字放到前边;
 */

3、插入排序

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-11 14:15
 * @Des 左程云-插入排序的实现
 */
public class InsertSort {
    public static void insertSort(int arr[]){
        if(arr == null || arr.length < 2){
            return;
        }
        for(int i=1;i<arr.length;i++){ //想要0~i范围内做到有序,直接从1开始就行,因为0位置上已经做到了有序
            System.out.println("i================="+i);
            for(int j=i-1;j>=0;j--){ //从后往前比较,然后交换。 j--无所谓了,毕竟不是--j
                System.out.println("j======="+j);
                if(arr[j]>arr[j+1]){  //不用担心j+1,因为j+1也不过=i而已,j到0就结束了。
                    System.out.println(""+j+"和"+(j+1)+"交换");
                    swap(arr,j,j+1);
                }
            }
        }
    }
    public static void swap(int[] arr,int i,int j){
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }
    
    public static void testFor(){
        for(int j=5;j>=0;j--){  // 5 4 3 2 1 0
            System.out.println(j);
        }
        for(int j=5;j>=0;--j){  // 5 4 3 2 1 0====> j=0 j>=0 --j  输出0,此时j=-1,不继续走了!
            System.out.println(j);
        }
        for(int j=5;j>0;--j){  // 5 4 3 2 1 
            System.out.println(j);
        }
    }

    public static void main(String[] args) {
//        int arr[] =  new int[]{4,1,7,3,4,9,5};
//        insertSort(arr);
//        for(int i=0;i<arr.length;i++){
//            System.out.println(arr[i]);
//        }
        testFor();
    }
}
/**
 * 插入排序稳定;
 */

4、二路归并排序

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-12 11:11
 * @Des 归并排序:准备一个辅助空间,谁小拷贝谁
 */
public class MergeSort {
    public static void mergeSort(int[] arr){
        if(arr == null || arr.length < 2){
            return;
        }
        process(arr,0,arr.length-1);
    }
    public static void process(int arr[],int L,int R){
        if(L == R){
            return;
        }
        int mid = L + ((R - L) >> 1);
        process(arr,L,mid); //现在左侧有序了?为什么说是有序了,因为现在是分拆成了单个元素,所以有序;
        process(arr,mid+1,R); //现在右侧有序了
        merge(arr,L,mid,R);  //左右两侧都有序了,需要一个merge的过程,再merge过程中就排好序了
    }
    public static void merge(int [] arr,int L,int M,int R){
        int[] help = new int[R-L+1];
        int i = 0;
        int p1 = L;
        int p2 = M+1;
        while (p1<=M && p2<=R){ //整体排序了
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        //左侧剩下的拷贝到help中
        while(p1 <= M){
            help[i++] = arr[p1++];
        }
        //右侧剩下的拷贝到help中
        while(p2 <= R){
            help[i++] = arr[p2++];
        }
        for(i = 0;i<help.length;i++){
            arr[L + i] = help[i];
        }
    }
}

 

5、快速排序

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-15 8:53
 * @Des 快速排序
 * <p>
 * 给定一个数组arr,和一个数num,请把小于等于num的数字放到左边,大于num的数字放到右边,要求额外空间复杂度为o(1),时间复杂度为o(n)
 * 3 5 6 7 4 3 5 8 假设num为5
 * (1)<=num时,[i]和 <=区域的下一个数字做交换,<=区域往右扩大一位,i++
 * (2) >num时,i++即可
 * 1th:<=区域为3
 * 2th:<=区域为3 5
 * 3th:不变i++
 * 4th:不变i++
 * 5th:<=区域为3 5 4
 * 6th:<=区域为3 5 4 3
 * 7th:<=区域为3 5 4 3 5
 * 8th:不变 i++
 * <p>
 * 给定一个数组arr,和一个数num,请把小于num的数字放到左边,等于num的数字放到中间,大于num的数字放到右边,要求额外空间复杂度为o(1),时间复杂度为o(n)
 * 1):[i]<num时,[i]和 <区域的下一个数字做交换, <区域 右扩, i++
 * 2):[i]==num时,i++
 * 2):[i]>num时,[i]和 >区域的上一个数字做交换,>区域左扩 i不变【为啥原地不动来,因为这个数字是从>区域上新过来的,还没有比较过。】
 * <p>
 * 整体有序的时候,使用快速排序 o(n^2) 因为每次只搞定了一个数字放在了最右边。划分值打的很偏,产生了最坏的情况!
 */

/**
 * 给定一个数组arr,和一个数num,请把小于等于num的数字放到左边,大于num的数字放到右边,要求额外空间复杂度为o(1),时间复杂度为o(n)
 *          3 5 6 7 4 3 5 8 假设num为5
 *          (1)<=num时,[i]和 <=区域的下一个数字做交换,<=区域往右扩大一位,i++
 *           (2) >num时,i++即可
 *         1th:<=区域为3
 *         2th:<=区域为3 5
 *         3th:不变i++
 *         4th:不变i++
 *         5th:<=区域为3 5 4
 *         6th:<=区域为3 5 4 3
 *         7th:<=区域为3 5 4 3 5
 *         8th:不变 i++
 *
 * 给定一个数组arr,和一个数num,请把小于num的数字放到左边,等于num的数字放到中间,大于num的数字放到右边,要求额外空间复杂度为o(1),时间复杂度为o(n)
 *         1):[i]<num时,[i]和 <区域的下一个数字做交换, <区域 右扩, i++
 *         2):[i]==num时,i++
 *         2):[i]>num时,[i]和 >区域的上一个数字做交换,>区域左扩 i不变【为啥原地不动来,因为这个数字是从>区域上新过来的,还没有比较过。】
 */

/**
 * 整体有序的时候,使用快速排序 o(n^2) 因为每次只搞定了一个数字放在了最右边。划分值打的很偏,产生了最坏的情况!
 */

/**
 * 快排1.0: question1
 * 快排2.0: question2
 * 快排3.0: 随机选 o(N*logN) :因为随机这件事情,变成了概率事件。 空间复杂度是o(logN)级别的
 */
public class QuickSort {
    public static void quickSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        quickSort(arr, 0, arr.length - 1);
    }

    //arr[l..r]排好序
    public static void quickSort(int[] arr, int L, int R) {
        if (L < R) {
            swap(arr, L + (int) (Math.random() * (R - L + 1)), R); //等概率随机选择一个位置,让它与最后一个位置做交换。

            System.out.println("(int) (Math.random() * (R - L + 1))======"+(int) (Math.random() * (R - L + 1)));
            
            int[] p = partition(arr, L, R); //划分值==区域的左边界和右边界
            quickSort(arr, L, p[0] - 1); // 小于区域上递归
            quickSort(arr, p[1] + 1, R); // 大于区域上递归
        }
    }

    //这是处理一个arr[1..r]的函数
    //默认以arr[r]做划分,arr[r]====p     <p      ==p         >p
    //返回等于区域(左边界,右边界),所以返回一个长度为2的数组res res[0] res[1]
    public static int[] partition(int[] arr, int L, int R) {
        int less = L - 1; //小于区域右边界,一开始就在最左边。数组位置左边的前一个位置。
        int more = R; //大于区域左边界,一开始就在最右边。数组位置右边的后一个位置。
        while (L < more) {  //L表示当前数字的位置 arr[R] -> 划分值
            if (arr[L] < arr[R]) { //当前数小于划分值
                swap(arr, ++less, L++);
            } else if (arr[L] > arr[R]) {
                swap(arr, --more, L);
            } else { //这是遍历的时候,等于标值的时候,L++
                L++;
            }
        }
        swap(arr, more, R);
        return new int[]{less + 1, more};
    }

    public static void swap(int arr[], int l, int r) {
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }

    public static void main(String[] args) {
        int arr[] = new int[]{2,5,3,8,4,32,45,21,13,45};
        quickSort(arr,0,arr.length - 1);
        for(int i=0;i<arr.length;i++){
            System.out.print(arr[i]+"\t");
        }
    }
}

6、递归行为解析

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-12 9:56
 * @Des 左程云-递归排序
 */

/**
 * 利用递归行为求arr上的最大值
 */
public class RecuisionSort {
    public static int getMax(int[] arr){
        return process(arr,0,arr.length-1);
    }
    //arr[L...R]范围上求最大值
    public static int process(int[] arr,int L,int R){
        if(L ==R){
            System.out.println("直接返回了"+arr[L]);
            return arr[L];
        }
        int mid = L + ((R - L) >> 1);
        int leftMax = process(arr,L,mid);
        System.out.println("leftMax=="+leftMax);
        int rightMax = process(arr,mid+1,R);
        System.out.println("rightMax=="+rightMax);
        System.out.println("最大值为:"+Math.max(leftMax,rightMax));
        return Math.max(leftMax,rightMax);
    }

    public static void main(String[] args) {
        int arr[] = new int[]{2,65,7,3,56,43};
        int maxValue = getMax(arr);
        System.out.println("最大值为=====》:"+maxValue);
        
    }
}
/**
 * 递归分析
 * [3,2,,5,6,7,4]       
 *                      process(0,5)
 *          p(0,2)                          p(3,5)
 *      p(0,1)    p(2,2)              p(3,4)        p(5,5)
 *  p(0,0)  p(1,1)                  p(3,3) p(4,4)
 *  
 *  结果拆分成了:p(0,0) p(1,1) p(2,2) p(3,3) p(4,4) p(5,5)  然后从底下往上汇总,才知道了process(0,5)的最大值是什么
 *  递归过程就是:用系统栈把整个过程给压栈了。【悬而未决的东西就压栈,算完了就出栈,相当于利用栈玩了个后续遍历。】
 */
/**
 * master公式:
 *  T(N)             =             a             *                   T(N/b)                      +   o(N^d)
 *  母问题的规模量是N           子问题调用次数                     子问题的都是N/b的规模的问题              除去子问题的调用之外,剩下的过程
 *                               2次                           子问题调用的时候规模等量,都是N/2          比大小 o(1)
 * master公式的时间复杂度计算: log2 2 > 0 所以process时间复杂度是 o(n^1) = o(n) ===>等于从左到右遍历求最大值,看上边注释的倒数第二行即可理解。
 * logbA < d  o(N^d)           
 * logbA > d  o(N^logbA)
 * logbA == d  o(N^d*logN)
 */                                                             

7、求逆序对问题

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-14 16:00
 * @Des 逆序对问题
 */

/**
 *   逆序对问题:在一个数字中,左边的数字如果比右边的数字大,则两个数字构成一个逆序对,请打印所有的逆序对
 *   例如:3,2,4,5,0,6,1  【逆序对数量 (3,2) (3,0) (3,1)| (2,0) (2,1)| (4,0) (4,1) |(5,0) (5,1) (6,1)】  ---10个
 *  
 */
public class ReverseOrderPair {
    public static int ReverseOrderPair(int[] arr) {
        if (arr == null || arr.length < 2) {
            return 0;
        }
        return process(arr, 0, arr.length - 1);
    }

    //arr[l...r]既要排好序,也要求小和
    public static int process(int[] arr, int l, int r) {
        if (l == r) {
            return 0;
        }
        int mid = l + ((r - l) >> 1);
        return process(arr, l, mid) + process(arr, mid + 1, r) + merge(arr, l, mid, r);
    }

    public static int merge(int[] arr, int l, int m, int r) {
        int[] help = new int[r - l + 1];
        int i = 0; 
        int p1 = l; 
        int p2 = m + 1; 
        int count = 0;
        while (p1 <= m && p2 <= r) {
//            count += arr[p1] > arr[p2] ? 1 : 0; //右边是排好序的啊
//            help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
//            if(arr[p1] > arr[p2]){
//                System.out.println("arr[p1]="+arr[p1]+"-----------------"+"arr[p2=]"+arr[p2]);
//                count += m - i + 1;
//            }

            if(arr[p1] <= arr[p2]){
                help[i++]=arr[p1++];
            }else{
                help[i++]=arr[p2++];
                count += m - p1 + 1;	//a[p1]>a[p2]时,p1到m所有数都和a[p2]形成逆序对  p1~m的的所有的数字大于p1 当然也大于p2
            }
        }
        while (p1 <= m) {
            help[i++] = arr[p1++];
        }
        while (p2 <= r) {
            help[i++] = arr[p2++];
        }
        for (i = 0; i < help.length; i++) {
            arr[l + i] = help[i];
        }
        return count;
    }

    public static void main(String[] args) {
        int arr[] = new int[]{3,2,4,5,0,6,1};
        int res = ReverseOrderPair(arr);
        System.out.println("res=="+res);

    }
}

8、求小和问题

package com.Mr.ZuoChengYun.sort;

/**
 * @author pshdhx
 * @date 2022-02-14 15:36
 * @Des 小和问题
 */
/**
 * 归并排序扩展:
 * 1、小和问题和逆序对问题
 * 小和问题:在一个数组中,每一个数字左边比当前树小的数累加起来,叫做这个数字的小和。求一个数组的小和;
 * 例如:【1,3,4,2,5】 1比左边比1小的数,没有; 3左边比3小的数:1 ; 4左边比4小的数 1 3; 2左边比2小的数字 1; 5左边比5小的数字 1 3 4 2 所以小和为 1+1+3+1+1+3+4+2=16;
 * 逆序对问题:在一个数字中,左边的数字如果比右边的数字大,则两个数字构成一个逆序对,请打印所有的逆序对
 * 例如:3,2,4,5,0  【逆序对数量 (3,2) (3,0) (2,0) (4,0) (5,0)】
 */
/**
 * 以小和问题为例子:右边有多少个数字比1大,有四个,那就是4个1相加;
 *                  1,3,4               2,5
 *             1,3        4           2    5
 *           1    3  
 */
public class SmallSum {
    public static int smallSum(int[] arr){
        if(arr == null || arr.length < 2){
            return 0;
        }
        return process(arr,0,arr.length-1);
    }
    //arr[l...r]既要排好序,也要求小和
    public static int process(int[] arr,int l,int r){
        if(l == r){
            return 0;
        }
        int mid = l + ((r-l)>>1);
        return process(arr,l,mid)+process(arr,mid+1,r)+merge(arr,l,mid,r);
    }
    public static int  merge(int[] arr,int l,int m,int r){
        int[] help = new int[r-l+1];
        int i = 0;
        int p1 = l;
        int p2 = m + 1;
        int res = 0;
        while(p1<=m && p2<=r){
            res += arr[p1] < arr[p2] ? (r-p2+1) * arr[p1]: 0; //右边是排好序的啊
            help[i++] = arr[p1] < arr[p2] ? arr[p1++] :arr[p2++];
        }
        while(p1 <= m){
            help[i++] = arr[p1++];
        }
        while(p2 <= r){
            help[i++] = arr[p2++];
        }
        for(i = 0;i<help.length;i++){
            arr[l+i] = help[i];
        }
        return res;
    }

    public static void main(String[] args) {
        int arr[] = new int[]{1,3,4,2,5};
        int res = smallSum(arr);
        System.out.println("res=="+res);
        
    }
}

9、对数器验证算法准确性

package com.Mr.ZuoChengYun.sort;

import java.util.Arrays;

/**
 * @Author pshdhx
 * @Description 左程云-对数器
 * @Date 2022/2/11 0011 22:45
 */
public class DuiShuQi {
    public static void comparator(int[] arr){
        Arrays.sort(arr);
    }
    public static int[] generateRandomArray(int maxSize,int maxValue){
        int arr[] = new int[(int)((maxSize+1)*Math.random())];
        for(int i=0;i<arr.length;i++){
            arr[i] = (int)((maxValue+1)*Math.random()) - (int)(maxValue*Math.random());
        }
        return arr;
    }
    public static int[] copyArray(int arr[]){
        if(arr == null){
            return null;
        }
        int[] res = new int[arr.length];
        for(int i=0;i<arr.length;i++){
            res[i] = arr[i];
        }
        return res;
    }
    public static  boolean isEqual(int[] arr1,int[] arr2){
        if((arr1==null && arr2 !=null) || (arr1 !=null && arr2 == null)){
            return false;
        }
        if(arr1 == null && arr2 == null){
            return true;
        }
        if(arr1.length != arr2.length){
            return false;
        }
        for(int i=0;i<arr1.length;i++){
            if(arr1[i] != arr2[i]){
                return false;
            }
        }
        return true;
    }
    public static void printArray(int arr[]){
        if(arr == null){
            return;
        }
        for(int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();

    }
    public static void insertionSort(int arr[]){
        if(arr == null || arr.length < 2){
            return;
        }
        for(int i=1;i<arr.length;i++){
            for(int j=i-1;j>=0;j++){
                if(arr[j]>arr[j+1]){
                    swap(arr,j,j+1);
                }
            }
        }
    }
    public static void swap(int[]arr,int i,int j){
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[i] ^ arr[j];
    }
    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 100;
        int maxValue = 100;
        boolean succeed = true;
        for(int i=0;i<testTime;i++){
            int[] arr1 = generateRandomArray(maxSize,maxValue);
            int[] arr2 = copyArray(arr1);
            insertionSort(arr1);
            comparator(arr2);
            if(!isEqual(arr1,arr2)){
                //打印arr1
                //打印arr2
                succeed = false;
                break;
            }
        }
        System.out.println(succeed ? "Nice !" : "Fucking fucked!");
        int[] arr = generateRandomArray(maxSize,maxValue);
        printArray(arr);
        insertionSort(arr);
        printArray(arr);
    }
}

10、二分法解释

package com.Mr.ZuoChengYun.sort;

/**
 * @Author pshdhx
 * @Description 二分法解决问题
 * @Date 2022/2/11 0011 20:28
 */
/**
 *问题一: 在一个有序数组中,查找某个数字是否存在
 */
/**
 * 问题二:在一个有序数组中,找>=某个数字最左侧的数字
 */
/**
 *问题三:在arr中,无序,任何相邻的数字一定不相等,局部最小:比左右两边的数字小就可以了。 
 */


/**
 * 无序也可以二分,谁说二分必须有序的啊?满足某种条件即可。
 * 如果0位置是局部最小,直接返回
 * 如果0位置不是局部最小,那么右边的数字一定比他大。↘
 * 再看最后边的位置:如果n-1位置是局部最小,就直接反悔了;如果不是,那么左边的数字一定比他小 ↙;
 * 根据最后边两个箭头的指向,这样的话,0位置到n-1位置上一定有个局部最小值;中间一定有个波谷是拐点的,他就是局部最小。
 * 取中间M位置,如果他是局部最小,直接返回。如果不是,那么他左侧或者右侧一定存在局部最小。从这里开始二分。
  */
//对数器的概念和使用

/**
 * 理解:如果一个想要测试的方法a,但是同一个问题可以由很多策略来实现。
  */
public class BinarySearch {

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值