常见的几种算法排序(C#)

总结下常见的几种排序及其实现,帮助自己加深记忆。

一、冒泡排序

1、原理: 通过依次比较相邻的元素,将较大(或较小)的元素交换到右侧,直到整个序列有序。

 public static int[] BuddleSort(int[] arry)
    { 
        int n= arry.Length;
        for (int i = 0; i < n; i++)
        {
            for (int j = 0; j < n - 1 - i; j++)
            {
                if (arry[j] < arry[j + 1])
                { 
                    int temp = arry[j];
                    arry[j] = arry[j + 1];
                    arry[j + 1]= temp;
                }
            }
        }
        return arry;
    }

2、算法步骤:
冒泡排序的基本步骤:
(1)遍历数组: 从第一个元素开始,依次比较相邻的两个元素。
(2)比较相邻元素: 比较当前元素和下一个元素的大小关系。
(3)交换位置: 如果当前元素大于下一个元素(升序排序),则交换它们的位置,否则不做任何操作。
(4)遍历次数: 完成一轮遍历后,最大(或最小)的元素就会沉到数组的最后一个位置。
(5)重复步骤: 重复执行以上步骤,直到数组中的所有元素都已经排序完成。

3、举例:
冒泡排序的每个步骤涉及多次比较和可能的交换操作。这里是对数组 [65, 24, 12, 32, 4, 15] 进行冒泡排序时的每个步骤的输出:
初始数组:[65, 24, 12, 32, 4, 15]
第1次内循环执行后:[24, 12, 32, 4, 15, 65]
第2次内循环执行后:[12, 24, 4, 15, 32, 65]
第3次内循环执行后:[12, 4, 15, 24, 32, 65]
第4次内循环执行后:[4, 12, 15, 24, 32, 65]
第5次内循环执行后:[4, 12, 15, 24, 32, 65]
因此,经过选择排序算法的执行后,数组变为 [4, 12, 15, 24, 32, 65]。
4、时间复杂度
冒泡排序的时间复杂度为 O(n^2)。

二、选择排序

1、原理: 每次从未排序的部分选择最小(或最大)的元素,放到已排序部分的末尾,直到整个序列有序。

public static int[] SelectSort(int[] arry)
    {
        int n = arry.Length;
        int temp;
        for (int i = 0; i < n - 1; i++)
        {
            int minIndex = i;
            for (int j = i+1; j < n - 1 ; j++)
            {
                if(arry[j]<arry[minIndex])
                    minIndex = j;                     
            }
            temp = arry[i];
            arry[i] = arry[minIndex];
            arry[minIndex] = temp;
        }

        return arry;
    }

2、算法步骤:
首先,找到数组中最小的元素,并将其与数组的第一个元素交换位置。
接下来,在剩余的未排序部分中找到最小的元素,并将其与数组的第二个元素交换位置。
以此类推,直到所有元素都被排序。

3、举例

比如我有个数组[65, 24, 12, 32, 4, 15],使用选择排序每次内循环执行后的输出内容如下:
初始状态:[65, 24, 12, 32, 4, 15]
第1次内循环执行后:[4, 24, 12, 32, 65, 15]
第2次内循环执行后:[4, 12, 24, 32, 65, 15]
第3次内循环执行后:[4, 12, 15, 32, 65, 24]
第4次内循环执行后:[4, 12, 15, 24, 65, 32]
第5次内循环执行后:[4, 12, 15, 24, 32, 65]
因此,经过选择排序算法的执行后,数组变为 [4, 12, 15, 24, 32, 65]。

4、时间复杂度
选择排序的时间复杂度为 O(n^2)。

三、插入排序

1、原理: 将数组分为已排序和未排序两部分,每次从未排序部分取出一个元素,插入到已排序部分的合适位置。
方法一:

 public static int[] InsertSort(int[] arry)
    {
        int n=arry.Length;
        for (int i = 1; i < n; i++)
        {
            int key = arry[i];
            int j = i - 1;
            while (j >= 0 && key > arry[j]) 
            {
                arry[j+1]=arry[j];
                j--;
            }
            arry[j+ 1] = key;
        }
        return arry;
    }

插入排序方法二,我把方法一中内循环结束把key赋值的地方修改到内循环中了,感觉这种更好理解,类似于交换数据,但可能效率没有方法一好。

 public static int[] InsertSort2(int[] arry)
    {
        int n = arry.Length;
        for (int i = 1; i < n; i++)
        {
            int key = arry[i];
            int j = i - 1;
            while (j >= 0 && key > arry[j])
            {
                arry[j+1] = arry[j];
                arry[j]= key;
                j--;
            }
        }
        return arry;
    }

2、算法步骤:
(1)从第一个元素开始,将该元素视为已排序部分。
(2)取出下一个元素,在已排序部分从后往前扫描。
(3)如果已排序部分的元素大于新元素,将该元素向右移动一个位置。
(4)重复步骤3,直到找到已排序部分的元素小于或等于新元素的位置。
(5)将新元素插入到找到的位置。
(6)重复步骤2到步骤5,直到所有元素都被插入到已排序部分。

3、举例

比如我有个数组[65, 24, 12, 32, 4, 15],使用插入排序每次内循环执行后的输出内容如下:
初始序列:[65, 24, 12, 32, 4, 15]
第一次循环后:[24, 65, 12, 32, 4, 15]
第二次循环后:[12, 24, 65, 32, 4, 15]
第三次循环后:[12, 24, 32, 65, 4, 15]
第四次循环后:[4, 12, 24, 32, 65, 15]
第五次循环后:[4, 12, 15, 24, 32, 65]
最终排序完成的序列为:[4, 12, 15, 24, 32, 65]

四、快速排序

1、原理
选择一个基准元素,将数组分成两部分,左边的部分小于基准,右边的部分大于基准,然后对左右两部分递归进行排序,直到整个数组有序。

public static int[] QuickSort(int[] arry,int low, int high)
    {
        if (low < high)
        { 
            int pivot =Partition(arry,low,high);
            QuickSort(arry,low,pivot-1);
            QuickSort(arry, pivot+1, high);
        }
        return arry;
    }

 public static int Partition(int[] arry, int low, int high)
    {
        int pivot = arry[low]; // 将第一个元素作为基准元素

        while (low < high)
        {
            // 从右向左找到一个小于基准元素的值
            while (low < high && arry[high] >= pivot)
            {
                high--;
            }
            // 将这个小于基准元素的值放到左侧
            arry[low] = arry[high];

            // 从左向右找到一个大于基准元素的值
            while (low < high && arry[low] <= pivot)
            {
                low++;
            }
            // 将这个大于基准元素的值放到右侧
            arry[high] = arry[low];
        }

        // 将基准元素放到正确的位置上
        arry[low] = pivot;

        // 返回基准元素的索引
        return low;
    }

2、算法步骤:
(1)选择基准元素: 从数组中选择一个基准元素,通常是数组的第一个元素、最后一个元素或者中间的元素。
(2)分区操作: 将数组中的元素按照基准元素的大小分成两部分,小于基准元素的放在基准元素的左边,大于基准元素的放在右边。分区操作完成后,基准元素的位置就确定了。
Partition函数中的While循环解释如下:选择arry[0]作为基准元素。

  • 双指针移动: 使用两个指针分别指向数组的起始位置和结束位置。左指针从左向右移动,直到找到一个大于或等于基准元素的值;右指针从右向左移动,直到找到一个小于或等于基准元素的值。
  • 交换元素: 一旦找到了左侧大于基准元素和右侧小于基准元素的值对,就交换它们的位置。这样,就保证了左侧的元素都小于等于基准元素,右侧的元素都大于等于基准元素。
  • 重复操作: 继续移动双指针,直到它们相遇为止。此时,所有小于基准元素的值都在基准元素的左侧,所有大于基准元素的值都在基准元素的右侧。
  • 基准元素归位: 将基准元素放置在正确的位置上,一般是与相遇点进行交换。
    (3)递归排序: 递归地对基准元素左右两边的子数组进行快速排序,直到子数组的大小为 0 或 1,即已经有序。
    (4)合并结果: 当所有的子数组都有序时,整个数组也就有序了。
    说明:网上有很多视频可以很好的解释这个算法的排序规则,可以在B站搜索看下。

3、时间复杂度
快速排序的平均时间复杂度是O(nlogn),但是在实际排序中,时间复杂度和基准元素(枢轴)的选择有关。如果枢轴选取不好,那么快速排序有可能就会退化为冒泡排序,时间复杂度为O(n*n)。

你可能感兴趣的:(C#数据结构与算法,算法,c#,排序算法)