C#十大排序算法

本文介绍了C#编程语言中常见的十大排序算法,包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序和基数排序,详细阐述了它们的工作原理、时间复杂度以及优缺点。这些排序算法在不同场景下有不同的性能表现,选择合适的排序算法取决于数据规模、分布和特定需求。
摘要由CSDN通过智能技术生成

在C#中,常见的十大排序算法包括:

1. 冒泡排序(Bubble Sort):

通过多次遍历数组,比较相邻元素并交换,使较大的元素逐渐"浮"到数组末尾。时间复杂度为O(n^2)。

  • 优点:实现简单,逻辑清晰。
  • 缺点:效率低下,时间复杂度为O(n^2)。
public static void BubbleSort(int[] nums)
{
    int n = nums.Length;
    bool swapped;
    
    for (int i = 0; i < n - 1; i++)
    {
        swapped = false;
        
        for (int j = 0; j < n - i - 1; j++)
        {
            if (nums[j] > nums[j + 1])
            {
                // 交换元素
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
                swapped = true;
            }
        }
        
        // 如果没有发生交换,则数组已经有序,提前结束排序
        if (!swapped)
            break;
    }
}

2. 选择排序(Selection Sort):

每次遍历数组,选择最小(或最大)的元素,与当前位置进行交换。时间复杂度为O(n^2)。

  • 优点:实现简单。
  • 缺点:时间复杂度为O(n^2),对大规模数据排序效率较低。
public static void SelectionSort(int[] nums)
{
    int n = nums.Length;
    
    for (int i = 0; i < n - 1; i++)
    {
        int minIndex = i;
        
        for (int j = i + 1; j < n; j++)
        {
            if (nums[j] < nums[minIndex])
            {
                minIndex = j;
            }
        }
        
        Swap(nums, i, minIndex);
    }
}

private static void Swap(int[] nums, int i, int j)
{
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

3. 插入排序(Insertion Sort):

将数组分为已排序和未排序两部分,逐个将未排序的元素插入到已排序部分的正确位置。时间复杂度为O(n^2)。

  • 优点:对于小规模或基本有序的数据排序效率较高。
  • 缺点:对大规模数据排序效率较低,时间复杂度为O(n^2)。
public static void InsertionSort(int[] nums)
{
    int n = nums.Length;

    for (int i = 1; i < n; i++)
    {
        int key = nums[i];
        int j = i - 1;

        while (j >= 0 && nums[j] > key)
        {
            nums[j + 1] = nums[j];
            j--;
        }

        nums[j + 1] = key;
    }
}

4. 希尔排序(Shell Sort):

插入排序的改进版,将数组分为多个子序列,分别进行插入排序,然后逐步减小子序列的长度。时间复杂度与步长序列的选择有关,平均为O(n^1.3)。

  • 优点:对中等规模数据排序效率较高。
  • 缺点:实现较为复杂,时间复杂度取决于步长序列的选择。
public static void ShellSort(int[] nums)
{
    int n = nums.Length;
    int gap = n / 2;

    while (gap > 0)
    {
        for (int i = gap; i < n; i++)
        {
            int temp = nums[i];
            int j = i;

            while (j >= gap && nums[j - gap] > temp)
            {
                nums[j] = nums[j - gap];
                j -= gap;
            }

            nums[j] = temp;
        }

        gap /= 2;
    }
}

5. 归并排序(Merge Sort):

将数组不断分割为两个子数组,递归地对子数组进行排序,然后将已排序的子数组合并。时间复杂度为O(nlogn)。

  • 优点:稳定且效率较高,时间复杂度为O(nlogn)。
  • 缺点:需要额外的内存空间进行合并操作。
public static void MergeSort(int[] nums)
{
    MergeSort(nums, 0, nums.Length - 1);
}

private static void MergeSort(int[] nums, int left, int right)
{
    if (left < right)
    {
        int mid = (left + right) / 2;

        MergeSort(nums, left, mid);
        MergeSort(nums, mid + 1, right);

        Merge(nums, left, mid, right);
    }
}

private static void Merge(int[] nums, int left, int mid, int right)
{
    int n1 = mid - left + 1;
    int n2 = right - mid;

    int[] leftArray = new int[n1];
    int[] rightArray = new int[n2];

    Array.Copy(nums, left, leftArray, 0, n1);
    Array.Copy(nums, mid + 1, rightArray, 0, n2);

    int i = 0, j = 0;
    int k = left;

    while (i < n1 && j < n2)
    {
        if (leftArray[i] <= rightArray[j])
        {
            nums[k] = leftArray[i];
            i++;
        }
        else
        {
            nums[k] = rightArray[j];
            j++;
        }
        k++;
    }

    while (i < n1)
    {
        nums[k] = leftArray[i];
        i++;
        k++;
    }

    while (j < n2)
    {
        nums[k] = rightArray[j];
        j++;
        k++;
    }
}

6. 快速排序(Quick Sort):

选择一个基准元素,将小于基准的元素放在左边,大于基准的元素放在右边,然后递归地对左右子数组进行排序。时间复杂度为O(nlogn)。

  • 优点:平均情况下效率较高,时间复杂度为O(nlogn)。
  • 缺点:最坏情况下效率较低,可能导致栈溢出。
public static void QuickSort(int[] nums)
{
    QuickSort(nums, 0, nums.Length - 1);
}

private static void QuickSort(int[] nums, int low, int high)
{
    if (low < high)
    {
        int pivotIndex = Partition(nums, low, high);
        QuickSort(nums, low, pivotIndex - 1);
        QuickSort(nums, pivotIndex + 1, high);
    }
}

private static int Partition(int[] nums, int low, int high)
{
    int pivot = nums[high];
    int i = low - 1;
    
    for (int j = low; j < high; j++)
    {
        if (nums[j] < pivot)
        {
            i++;
            Swap(nums, i, j);
        }
    }
    
    Swap(nums, i + 1, high);
    return i + 1;
}

private static void Swap(int[] nums, int i, int j)
{
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

7. 堆排序(Heap Sort):

构建最大堆或最小堆,将堆顶元素与末尾元素交换并重新调整堆,重复此过程直到整个数组有序。时间复杂度为O(nlogn)。

  • 优点:不受输入数据的初始状态影响,时间复杂度为O(nlogn)。
  • 缺点:实现较为复杂,需要维护堆结构。
public static void HeapSort(int[] nums)
{
    int n = nums.Length;

    // 构建最大堆
    for (int i = n / 2 - 1; i >= 0; i--)
    {
        Heapify(nums, n, i);
    }

    // 逐个将最大元素移到末尾,并调整堆
    for (int i = n - 1; i >= 0; i--)
    {
        // 将当前最大元素(根节点)与末尾元素交换
        int temp = nums[0];
        nums[0] = nums[i];
        nums[i] = temp;

        // 调整堆,使剩余元素重新满足最大堆的性质
        Heapify(nums, i, 0);
    }
}

private static void Heapify(int[] nums, int n, int i)
{
    int largest = i;
    int leftChild = 2 * i + 1;
    int rightChild = 2 * i + 2;

    // 找到左子节点和右子节点中的最大值
    if (leftChild < n && nums[leftChild] > nums[largest])
    {
        largest = leftChild;
    }

    if (rightChild < n && nums[rightChild] > nums[largest])
    {
        largest = rightChild;
    }

    // 如果最大值不是当前节点,则交换并继续调整堆
    if (largest != i)
    {
        int temp = nums[i];
        nums[i] = nums[largest];
        nums[largest] = temp;

        Heapify(nums, n, largest);
    }
}

8. 计数排序(Counting Sort):

统计每个元素的出现次数,然后根据统计信息将元素放入正确的位置,实现线性时间复杂度O(n+k),其中k为元素的取值范围。

  • 优点:适用于数据范围较小且重复值较多的情况,时间复杂度为O(n+k)。
  • 缺点:需要额外的内存空间。
public static void CountingSort(int[] nums)
{
    int n = nums.Length;

    // 找到待排序数组的最大值和最小值
    int minVal = int.MaxValue;
    int maxVal = int.MinValue;
    for (int i = 0; i < n; i++)
    {
        if (nums[i] < minVal)
        {
            minVal = nums[i];
        }
        if (nums[i] > maxVal)
        {
            maxVal = nums[i];
        }
    }

    // 创建计数数组,用于记录每个元素的出现次数
    int[] count = new int[maxVal - minVal + 1];

    // 统计每个元素的出现次数
    for (int i = 0; i < n; i++)
    {
        count[nums[i] - minVal]++;
    }

    // 根据计数数组重构排序后的数组
    int index = 0;
    for (int i = 0; i < count.Length; i++)
    {
        while (count[i] > 0)
        {
            nums[index] = i + minVal;
            index++;
            count[i]--;
        }
    }
}

9. 桶排序(Bucket Sort):

将元素分配到不同的桶中,对每个桶中的元素进行排序,然后按顺序合并各个桶的元素,时间复杂度取决于桶的个数和元素分布的情况。

  • 优点:适用于数据分布均匀的情况,时间复杂度为O(n)。
  • 缺点:对于数据分布不均匀的情况效率较低。
public static void BucketSort(float[] nums)
{
    int n = nums.Length;

    // 创建桶数组
    List<float>[] buckets = new List<float>[n];

    // 将元素分配到不同的桶中
    for (int i = 0; i < n; i++)
    {
        int bucketIndex = (int)(n * nums[i]);
        if (buckets[bucketIndex] == null)
        {
            buckets[bucketIndex] = new List<float>();
        }
        buckets[bucketIndex].Add(nums[i]);
    }

    // 对每个桶中的元素进行排序
    for (int i = 0; i < n; i++)
    {
        if (buckets[i] != null)
        {
            buckets[i].Sort();
        }
    }

    // 合并各个桶中的元素得到最终排序结果
    int index = 0;
    for (int i = 0; i < n; i++)
    {
        if (buckets[i] != null)
        {
            foreach (float num in buckets[i])
            {
                nums[index] = num;
                index++;
            }
        }
    }
}

10. 基数排序(Radix Sort):

根据元素的位值(个位、十位、百位等)进行排序,通过多次迭代和计数排序实现,时间复杂度取决于迭代次数和计数排序的时间复杂度。
- 优点:适用于整数排序,时间复杂度为O(nk),其中k为数字的位数。
- 缺点:需要额外的内存空间。

public static void RadixSort(int[] nums)
{
    int n = nums.Length;

    // 找到最大值,确定迭代次数
    int maxVal = nums[0];
    for (int i = 1; i < n; i++)
    {
        if (nums[i] > maxVal)
        {
            maxVal = nums[i];
        }
    }

    // 进行基数排序
    for (int exp = 1; maxVal / exp > 0; exp *= 10)
    {
        CountingSortByDigit(nums, n, exp);
    }
}

private static void CountingSortByDigit(int[] nums, int n, int exp)
{
    int[] output = new int[n];
    int[] count = new int[10];

    // 统计每个数字的出现次数
    for (int i = 0; i < n; i++)
    {
        int digit = (nums[i] / exp) % 10;
        count[digit]++;
    }

    // 计算累计次数
    for (int i = 1; i < 10; i++)
    {
        count[i] += count[i - 1];
    }

    // 根据统计信息将元素放入正确的位置
    for (int i = n - 1; i >= 0; i--)
    {
        int digit = (nums[i] / exp) % 10;
        output[count[digit] - 1] = nums[i];
        count[digit]--;
    }

    // 将排序后的结果复制回原数组
    Array.Copy(output, 0, nums, 0, n);
}

每种排序算法都有自己的适用场景和性能特点,选择合适的排序算法取决于数据规模、数据分布以及对稳定性、内存占用等要求的考量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不敲注释的呆呆

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

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

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

打赏作者

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

抵扣说明:

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

余额充值