【算法】C#实现经典排序算法总结(附动图)

简介: 自行整理的C#常见算法笔记。

前言

大家好,这是自己整理的C#常见排序算法笔记,方便自己学习的同时分享出来,感谢支持。

1. 冒泡排序

  • 重复遍历数组比较相邻的元素,如果前面的比后面的大,就交换它们两个
  • 每次遍历整个数组,遍历完成后,下一次遍历的索引减一(范围往左缩一位)
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较,则排序完成

1.1 动态展示:

在这里插入图片描述

1.2 算法实现:

public class Solution
{
    // 冒泡排序   
    static void bubbleSort(int[] nums)
    {
        int n = nums.Length;    // 得到数组的长度     
        for (int i = 0; i < n; i++)
        {
            bool flag = false;      // 表示本轮是否有进行变量交换
            for (int idx = 0; idx < n - i - 1; idx++)
            {
                if (nums[idx] > nums[idx + 1])
                {
                    int temp = nums[idx];
                    nums[idx] = nums[idx + 1];
                    nums[idx + 1] = temp;
                    flag = true;
                }
            }
            // 如果flag为False,那说明本轮排序没有进行任何变量交换
            // 数组已经是有序的了
            if (!flag)
            {
                break;
            }
        }
    }

    static void Main()
    {
        int[] test = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
        bubbleSort(test);

        foreach (int item in test)
        {
            Console.WriteLine(item);
        }
    }
}

2. 选择排序

  • 数组的初始状态:有序区为空,无序区为[0,…,n]
  • 每次找到无序区里的最小元素,添加到有序区的最后
  • 重复前面步骤,直到整个数组排序完成

2.1 动态展示:

在这里插入图片描述

2.2 算法实现:

public class Solution
{
    // 选择排序 
    public static void selectionSort(int[] nums)
    {
        int n = nums.Length;
        for (int i = 0; i < n; i++)
        {
            // 找无序区中最小的元素 
            int minIndex = i;  //无序区中的最小元素的索引
            for (int j = i + 1; j < n; j++)
            {
                if (nums[j] < nums[minIndex])
                {
                    minIndex = j;
                }
            }
            // 执行完上面的循环后
            // minIndex就是无序区中的最小元素的索引
            // 把最小元素和有序区的后一个元素交换位置
            int temp = nums[i];
            nums[i] = nums[minIndex];
            nums[minIndex] = temp;
        }
    }

    static void Main()
    {
        int[] test = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
        selectionSort(test);
        foreach (int item in test)
        {
            Console.WriteLine(item);
        }
    }
}

3. 插入排序

  • 整个数组分为左边部分有序元素集合右边部分无序元素集合
  • 一开始从索引为一的元素开始逐渐递增与有序集合进行比较
  • 将未排序的元素一个一个地插入到有序的集合中,插入时把所有有序集合从后向前扫一遍,找到合适的位置插入

3.1 动态展示:

在这里插入图片描述

3.2 算法实现:

public class Solution
{
    // 插入排序 
    public static void insertSort(int[] nums)
    {
        int n = nums.Length;     // 数组的长度

        for (int i = 0; i < n - 1; i++)
        {
            int curNum = nums[i + 1];     // 无序区的第一个元素 
            int idx = i;    //有序区的最后一个元素的索引

            while (idx >= 0 && nums[idx] > curNum)
            {
                nums[idx + 1] = nums[idx];  // 把有序区的元素往后挪一位
                idx -= 1;
            }
            nums[idx + 1] = curNum;
        }
    }

    static void Main()
    {
        int[] test = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
        insertSort(test);
        foreach (int item in test)
        {
            Console.WriteLine(item);
        }
    }
}

4. 快速排序

  • 开始设定一个基准值pivot
  • 将数组重新排列,所有比pivot小的放在其前面比pivot大的放后面,这种操作称为分区(partition)操作
  • 对两边的数组重复前两个步骤; (分而治之算法思想)

4.1 动态展示:

在这里插入图片描述

4.2 算法实现:

public class Solution
{
    // 分区
    public static int partition(int[] nums, int left, int right)
    {
        int pivot = nums[left];     // 区域的第一个元素作为基准值

        while (left < right)
        {
            while (left < right && nums[right] > pivot)
                right--;
            nums[left] = nums[right];

            while (left < right && nums[left] <= pivot)
                left++;
            nums[right] = nums[left];
        }
        nums[right] = pivot;        // 基准值的正确位置 

        return left;        // 返回基准值 
    }

    // 快速排序 
    public static void quickSort(int[] nums, int left, int right)
    {
        if (left >= right)
            return;

        // 分区 --> 分好区之后的基准值的索引 
        int pivotIndex = partition(nums, left, right);
        // 左边的区域, left -> pivotIndex - 1 
        quickSort(nums, left, pivotIndex - 1);
        // 右边的区域,pivotIndex+1->right 
        quickSort(nums, pivotIndex + 1, right);
    }


    static void Main()
    {
        int[] test = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
        quickSort(test, 0, test.Length - 1);
        foreach (int item in test)
        {
            Console.WriteLine(item);
        }
    }
}

5. 随机快速排序

  • 将一个待排序的数组随机选择“基准”(pivot)分割成两个独立的子数组
  • 其中一部分的所有元素都比另外一部分的所有元素都要小
  • 按此方法对这两部分元素分别进行快速排序
  • 整个排序过程可以递归进行,以此达到整个数据变成有序序列

5.1 动态展示:

在这里插入图片描述

5.2 算法实现:

public class Solution
{
    // 随机分区 
    public static int randomPartition(int[] nums, int left, int right)
    {
        Random random = new Random();   // 随机生成数 
        int i = random.Next(left,right);
        int temp = nums[i];
        nums[i] = nums[right];
        nums[right] = nums[i];

        return partition(nums, left, right); 
    }

    // 分区 
    public static int partition(int[] nums, int left, int right)
    {
        int pivot = nums[left];     // 区域的第一个元素作为基准值

        while(left < right)
        {
            while (left < right && nums[right] > pivot)
                right--;
            nums[left] = nums[right];
                                    
            while (left < right && nums[left] <= pivot)
                left++;
            nums[right] = nums[left];
        }
        nums[right] = pivot;        // 基准值的正确位置 

        return left;        // 返回基准值 
    }

    // 快速排序 
    public static void quickSort(int[] nums, int left, int right)
    {
        if (left >= right)
            return; 

        // 分区 --> 分好区之后的基准值的索引 
        int pivotIndex = randomPartition(nums, left, right);
        // 左边的区域, left -> pivotIndex - 1 
        quickSort(nums, left, pivotIndex - 1);
        // 右边的区域,pivotIndex+1->right 
        quickSort(nums, pivotIndex+1, right);  
    }


    static void Main()
    {
        int[] test = { 44, 12, 59, 36, 62, 43, 94, 7, 35, 52, 85 };
        quickSort(test, 0, test.Length-1); 
        foreach(int item in test)
        {
            Console.WriteLine(item);
        }
    }
}

6. 归并排序

  • 归并排序算法是采用分治法的一个非常典型的应用
  • 不断将数组一分为二,一直分到子数组的长度小于等于一(无法再分)
  • 通过递归的方法逐个比较大小自底向上有序地合并数组

6.1 动态展示:

在这里插入图片描述

6.2 算法实现:

public class Solution
{
    // 合并
    public static int[] merge(int[] left, int[] right)
    {
        // 最终返回一个合并好的有序的数组
        // 定义两个变量,分别代表当前left与right的未添加进有序数组的第一个元素
        int leftIndex = 0, rightIndex = 0;
        List<int> res = new List<int>();        
       
        while (leftIndex < left.Length && rightIndex < right.Length)
        {
            //左边数组的元素小于右边数组 
            if (left[leftIndex] <= right[rightIndex])
            {
                // 把左边元素添加到有序区中 
                res.Add(left[leftIndex]);
                // 索引往后移
                leftIndex ++; 
            }
            else
            {
                // 把右边元素添加到有序区中 
                res.Add(right[rightIndex]);
                // 索引往后移 
                rightIndex ++; 
            }
        }

        // 把剩余的未添加的元素全部添加到有序数组后面
        foreach (int item in right[rightIndex..])    
        {
            res.Add(item);
        }

        // 为什么可以直接添加?因为left,right本身就是一个有序数组
        foreach (int item in left[leftIndex..])      
        {
            res.Add(item);
        }
        // 如果说left_idx走完了,right还剩一些元素,说明right剩下的元素全部都比有序数组的最后一个元素要大
        return res.ToArray();
    }

    // 归并排序
    public static int[] mergeSort(int[] nums)
    {
        //数组不能再分了
        if (nums.Length <= 1)
            return nums;
        int mid = nums.Length / 2;      // 求出数组的中间位置 
        
        int[] left = mergeSort(nums[..mid]);
        int[] right = mergeSort(nums[mid..]);
   
        // 合并
        return merge(left, right); 
    }

    // 归并排序 
    static void Main(string[] args)
    {
        int[] nums = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48}; 
        int[] result = mergeSort(nums);
        foreach(int item in result)
        {
            Console.WriteLine(item);
        }
    }
}

7. 计数排序

  • 找出待排序的数组中最大最小的元素
  • 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  • 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  • 向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

7.1 动态展示:

在这里插入图片描述

7.2 算法实现:

public class Program
{
    // 计数排序
    public static void CountingSort(int[] array)
    {
        // 长度小于等于0直接return
        if (array.Length <= 0) 
            return;

        int min = array[0];     
        int max = min;
        // 找出数组最大元素和最小元素 
        foreach (int item in array)
        {
            if (item > max)
            {
                max = item;
            }
            else if (item < min)
            {
                min = item;
            }
        }

        // 把所有元素存入counting数组 
        int[] counting = new int[max - min + 1];
        for (int i = 0; i < array.Length; i++)
        {
            counting[array[i] - min] += 1;
        }

        int index = -1;
        for (int i = 0; i < counting.Length; i++)
        {
            for (int j = 0; j < counting[i]; j++)
            {
                index++;
                array[index] = i + min;
            }
        }
    }


    public static void Main(string[] args)
    {
        int[] array = { 43, 69, 11, 72, 28, 21, 56, 80, 48, 94, 32, 8 };
        CountingSort(array);

        foreach(int item in array)
        {
            Console.WriteLine(item);
        }
    } 
}

8. 基数排序

  • 基数排序是一种非比较型排序利用桶的算法,直接利用每个位数作为下标放入桶中,无需与其他元素比较大小
  • 将待比较的数字从较低位到较高位依次放入桶中, 再按照桶的顺序取出
  • 直到最长位数的数字被完全比较,即可得到已排序的数组

8.1 动态展示:

在这里插入图片描述

8.2 算法实现:

public class Program
{
    // 基数排序 
    public static void RadixSort(int[] array, int bucketNum)
    {
        int maxLength = MaxLength(array);

        //创建bucket时,在二维中增加一组标识位,
        //其中bucket[x, 0]表示这一维所包含的数字的个数
        //通过这样的技巧可以少写很多代码
        int[,] bucket = new int[bucketNum, array.Length + 1];

        for (int i = 0; i < maxLength; i++)
        {
            foreach (var num in array)
            {
                int bit = (int)(num / Math.Pow(10, i) % 10);
                bucket[bit, ++bucket[bit, 0]] = num;
            }

            for (int count = 0, j = 0; j < bucketNum; j++)
            {
                for (int k = 1; k <= bucket[j, 0]; k++)
                {
                    array[count++] = bucket[j, k];
                }
            }

            //最后要重置这个标识
            for (int j = 0; j < bucketNum; j++)
            {
                bucket[j, 0] = 0;
            }

        }
    }

    private static int MaxLength(int[] array)
    {
        if (array.Length <= 0) 
            return 0;
            
        int max = array.Max();      // 取出数组的最大值 
        return (int)Math.Log10(max) + 1;    // 取出位数 
    }


    public static void Main(string[] args)
    {
        int[] array = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
        RadixSort(array, 10);
        foreach (int item in array)
        {
            Console.WriteLine(item);
        }
    }
}

9. 桶排序

  • 桶排序是计数排序的升级版
  • 根据数组的最大值与最小值申请一些桶(生成对应的序列)
  • 将数组的元素放入桶中,并保证桶里是有序的
  • 合并每个桶,得到的就是一个有序的序列

9.1 动态展示:

在这里插入图片描述

9.2 算法实现:

public class Program
{
    // 桶排序
    static void BucketSort(int[] array, int bucketsize = 5)
    {
        int max = array.Max(), min = array.Min();               // 最大值与最小值
        int bucketnums = (max - min) / bucketsize + 1;           // 分配的桶数量

        List<List<int>> buckets = new List<List<int>>();
        // 生成桶
        for (int i = 0; i < bucketnums; i++)
        {
            buckets.Add(new List<int>());
        }

        //将数组的元素放入桶中,并保证桶里是有序的        
        for (int i = 0; i < array.Length; i++)
        {
            int bucketIndex = (array[i] - min) / bucketsize;
            buckets[bucketIndex].Add(array[i]);
        }

        int index = 0;
        for (int i = 0; i < buckets.Count; i++)
        {
            buckets[i].Sort();      // 对生成的每个桶排序 
            for (int j = 0; j < buckets[i].Count; j++)
            {
                array[index++] = buckets[i][j];
            }
        }
    }
    
    static void Main(string[] args)
    {
        int[] test = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
        BucketSort(test); 
        
        foreach(int item in test)
        {
            Console.WriteLine(item);
        }
    }
}

10. 堆排序

  • 将待排序的数组构造成一个大顶堆,整个数组的最大值就是堆结构的顶端
  • 将顶端的数与末尾的数交换,则末尾的数为最大值,剩余待排序数组个数为n-1(n为数组长度)
  • 将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
  • 注意:升序用大根堆,降序就用小根堆(默认为升序)

10.1 动态展示:

在这里插入图片描述

10.2 算法实现:

class Program
{
    // 堆排序 
    public static void HeapSort(int[] nums)
    {
        //将最大的值推到堆顶
        //x根据最后一个子节点的位置计算出父节点
        int x = Convert.ToInt32(Math.Floor(Convert.ToDouble((nums.Length - 2) / 2)));
        for (int i = x; i >= 0; i--)
        {
            //如果子元素只存在左子元素时, 让右子元素等于左子元素
            while (nums[i] < nums[i * 2 + 1] || nums[i] < nums[(i * 2 + 2) > (nums.Length - 1) ? (i * 2 + 1) : i * 2 + 2])
            {
                if (nums[i * 2 + 1] >= nums[(i * 2 + 2) > (nums.Length - 1) ? (i * 2 + 1) : i * 2 + 2])
                {
                    int index = nums[i];
                    nums[i] = nums[i * 2 + 1];
                    nums[i * 2 + 1] = index;
                }
                else
                {
                    int index = nums[i];
                    nums[i] = nums[i * 2 + 2];
                    nums[i * 2 + 2] = index;
                }
            }
        }
        //输出堆顶最大的元素
        int max = nums[0];
        nums[0] = nums[nums.Length - 1];
        Console.WriteLine(max);
        //将数组中的最后一个元素删除
        int[] num = new int[nums.Length - 1];
        for (int j = 0; j < nums.Length - 1; j++)
        {
            num[j] = nums[j];
        }

        Adjust(num);
    }


    public static void Adjust(int[] nums)
    {
        if (nums.Length > 1) HeapSort(nums);
        else Console.WriteLine("{0}\t", nums[0]);
    }

    static void Main(string[] args)
    {
        int[] nums = new int[] { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
        Adjust(nums);
    }
}

11. 希尔排序

  • 希尔排序是插入排序的优化版本
  • 设定一个增量gap,将数组按照gap分组
  • 依次对每一组进行插入排序
  • 缩小增量gap,重复前两个步骤,直到gap缩小到一,那么最后一次排序就是

插入排序

11.1 动态展示 :

在这里插入图片描述

11.2 算法实现 :

public class Program
{
    // 希尔排序 
    public static void shellSort(int[] nums)
    {
        int n = nums.Length;    // 数组的长度 
        int gap = n / 2;    // 设定一个增量gap 
        
        while(gap >= 1)
        {
            // 分组
            for(int i = gap; i < n; i++) 
            {
                int curNum = nums[i];   // 当前要插入的无序区的元素的值
                int idx = i - gap;      // 当前元素所在小组的有序区的最后一个元素的索引 
                while (idx >= 0 && curNum < nums[idx])      // 插入排序
                {
                    nums[idx + gap] = nums[idx];
                    idx -= gap;
                }
                nums[idx+gap] = curNum; 
            }
            gap /= 2;   // 缩小增量 
        }
    }
     
   
    static void Main(string[] args)
    {
        int[] test = { 3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48 };
        shellSort(test); 
        foreach(int item in test)
        {
            Console.WriteLine(item);
        }
    }
}

总结:

排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 稳定性
冒泡排序 O(n^2^) O(n) O(n^2^) O(1) 稳定
选择排序 O(n^2^) O(n^2^) O(n^2^) O(1) 不稳定
插入排序 O(n^2^) O(n) O(n^2^) O(1) 稳定
快速排序 O(nlog^n^) O(nlog^n^) O(n^2^) O(log^n^) 不稳定
随机快速排序 O(n^2^) O(nlg^n^) O(n^2^) O(1) 不稳定
归并排序 O(nlog^n^) O(nlog^n^) O(nlog^n^) O(n) 稳定
计数排序 O(n + k) O(n + k) O(n + k) O(k) 稳定
基数排序 O(n$\times$k) O(n$\times$k) O(n$\times$k) O(n + k) 稳定
桶排序 O(n + k) O(n + k) O(n^2^) O(n + k) 稳定
堆排序 O(nlog^n^) O(nlog^n^) O(nlog^n^) O(1) 不稳定
希尔排序 O(nlog^n^) O(nlog^2^n) O(nlog^2^n) O(1) 稳定

注意

本章部分图文内容来源于网站资料和网友提供, 自己整理收集,方便学习参考,版权属于 原作者
目录
相关文章
|
6月前
|
存储 监控 算法
电脑监控管理中的 C# 哈希表进程资源索引算法
哈希表凭借O(1)查询效率、动态增删性能及低内存开销,适配电脑监控系统对进程资源数据的实时索引需求。通过定制哈希函数与链地址法冲突解决,实现高效进程状态追踪与异常预警。
292 10
|
11月前
|
存储 算法 安全
如何控制上网行为——基于 C# 实现布隆过滤器算法的上网行为管控策略研究与实践解析
在数字化办公生态系统中,企业对员工网络行为的精细化管理已成为保障网络安全、提升组织效能的核心命题。如何在有效防范恶意网站访问、数据泄露风险的同时,避免过度管控对正常业务运作的负面影响,构成了企业网络安全领域的重要研究方向。在此背景下,数据结构与算法作为底层技术支撑,其重要性愈发凸显。本文将以布隆过滤器算法为研究对象,基于 C# 编程语言开展理论分析与工程实践,系统探讨该算法在企业上网行为管理中的应用范式。
306 8
|
12月前
|
机器学习/深度学习 监控 算法
员工上网行为监控软件中基于滑动窗口的C#流量统计算法解析​
在数字化办公环境中,员工上网行为监控软件需要高效处理海量网络请求数据,同时实时识别异常行为(如高频访问非工作网站)。传统的时间序列统计方法因计算复杂度过高,难以满足低延迟需求。本文将介绍一种基于滑动窗口的C#统计算法,通过动态时间窗口管理,实现高效的行为模式分析与流量计数。
338 2
|
10月前
|
监控 算法 数据处理
内网实时监控中的 C# 算法探索:环形缓冲区在实时数据处理中的关键作用
本文探讨了环形缓冲区在内网实时监控中的应用,结合C#实现方案,分析其原理与优势。作为固定长度的循环队列,环形缓冲区通过FIFO机制高效处理高速数据流,具备O(1)时间复杂度的读写操作,降低延迟与内存开销。文章从设计逻辑、代码示例到实际适配效果展开讨论,并展望其与AI结合的潜力,为开发者提供参考。
404 2
|
10月前
|
监控 算法 安全
公司电脑监控软件关键技术探析:C# 环形缓冲区算法的理论与实践
环形缓冲区(Ring Buffer)是企业信息安全管理中电脑监控系统设计的核心数据结构,适用于高并发、高速率与短时有效的多源异构数据处理场景。其通过固定大小的连续内存空间实现闭环存储,具备内存优化、操作高效、数据时效管理和并发支持等优势。文章以C#语言为例,展示了线程安全的环形缓冲区实现,并结合URL访问记录监控应用场景,分析了其在流量削峰、关键数据保护和高性能处理中的适配性。该结构在日志捕获和事件缓冲中表现出色,对提升监控系统效能具有重要价值。
292 1
|
9月前
|
存储 搜索推荐 算法
加密算法、排序算法、字符串处理及搜索算法详解
本文涵盖四大类核心技术知识。加密算法部分介绍了对称加密(如 AES)、非对称加密(如 RSA)、哈希摘要(如 SHA-2)、签名算法的特点及密码存储方案(加盐、BCrypt 等)。 排序算法部分分类讲解了比较排序(冒泡、选择、插入、归并、快排、堆排序)和非比较排序(计数、桶、基数排序)的时间复杂度、适用场景及实现思路,强调混合排序的工业应用。 字符串处理部分包括字符串反转的双指针法,及项目中用正则进行表单校验、网页爬取、日志处理的实例。 搜索算法部分详解了二分查找的实现(双指针与中间索引计算)和回溯算法的概念(递归 + 剪枝),以 N 皇后问题为例说明回溯应用。内容全面覆盖算法原理与实践
267 0
|
11月前
|
存储 监控 算法
局域网上网记录监控的 C# 基数树算法高效检索方案研究
在企业网络管理与信息安全领域,局域网上网记录监控是维护网络安全、规范网络行为的关键举措。随着企业网络数据量呈指数级增长,如何高效存储和检索上网记录数据成为亟待解决的核心问题。基数树(Trie 树)作为一种独特的数据结构,凭借其在字符串处理方面的卓越性能,为局域网上网记录监控提供了创新的解决方案。本文将深入剖析基数树算法的原理,并通过 C# 语言实现的代码示例,阐述其在局域网上网记录监控场景中的具体应用。
245 7
|
存储 搜索推荐 算法
算法系列之排序算法-堆排序
堆排序(Heap Sort)是一种基于堆数据结构的比较排序算法。它的时间复杂度为 $O(nlogn)$,并且是一种原地排序算法(即不需要额外的存储空间)。堆排序的核心思想是利用堆的性质来维护一个最大堆或最小堆,然后逐步将堆顶元素(最大值或最小值)取出,放到数组的末尾,最终得到一个有序的数组。
408 8
算法系列之排序算法-堆排序
|
算法 搜索推荐 测试技术
【调度算法】快速非支配排序算法
【调度算法】快速非支配排序算法
437 3
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
下一篇
开通oss服务