在 C 语言中实现快速排序(Quick Sort)有多种方式。我们可以通过不同的 划分策略 或 递归处理方式 实现不同版本的快速排序。下面将展示三种常见版本的快速排序实现:
快速排序基本原理
快速排序是基于分治法的排序算法。基本步骤如下:
- 选择基准:从待排序的数组中选择一个元素作为基准(pivot)。
 - 划分操作:将数组分为两部分,一部分小于基准,另一部分大于基准。
 - 递归排序:递归地对两个子数组进行快速排序,直到子数组的大小为 1 或 0。
 
三种版本的快速排序实现
我们将实现三种版本的快速排序:
- 经典版本:选择基准元素为第一个元素。
 - 随机基准版本:选择基准元素为数组中的随机元素。
 - 三路划分版本:针对重复元素较多的情况,使用三路划分方法(荷兰国旗问题)。
 
1. 经典快速排序
经典的快速排序算法选择数组的第一个元素作为基准,并通过 一次划分 将数组分为两个部分。
代码实现:
#include <stdio.h>
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
int partition(int arr[], int low, int high) {
    int pivot = arr[low];  // 选择第一个元素为基准
    int left = low + 1;
    int right = high;
    while (left <= right) {
        // 左边找到大于或等于基准的元素
        while (left <= right && arr[left] <= pivot) left++;
        // 右边找到小于或等于基准的元素
        while (left <= right && arr[right] > pivot) right--;
        if (left < right) {
            swap(&arr[left], &arr[right]);
        }
    }
    // 将基准元素放到正确的位置
    swap(&arr[low], &arr[right]);
    return right;
}
void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pivotIndex = partition(arr, low, high);
        quickSort(arr, low, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, high);
    }
}
int main() {
    int arr[] = {10, 80, 30, 90, 40, 50, 70};
    int n = sizeof(arr) / sizeof(arr[0]);
    quickSort(arr, 0, n - 1);
    printf("Sorted array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}
关键点:
- 选择基准:选择第一个元素作为基准。
 - 划分过程:将数组分为两部分,一部分小于基准,另一部分大于基准。
 
2. 随机基准版本
在经典快速排序中,选择第一个元素作为基准可能导致最坏的时间复杂度 O(n^2),特别是当输入数据已经接近有序时。为此,可以通过 随机选择基准元素 来提高算法的性能,避免最坏情况。
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
int partition(int arr[], int low, int high) {
    // 随机选择基准
    int pivotIndex = low + rand() % (high - low + 1);
    swap(&arr[low], &arr[pivotIndex]);  // 将基准放到第一个位置
    int pivot = arr[low];
    int left = low + 1;
    int right = high;
    while (left <= right) {
        while (left <= right && arr[left] <= pivot) left++;
        while (left <= right && arr[right] > pivot) right--;
        if (left < right) {
            swap(&arr[left], &arr[right]);
        }
    }
    swap(&arr[low], &arr[right]);
    return right;
}
void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pivotIndex = partition(arr, low, high);
        quickSort(arr, low, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, high);
    }
}
int main() {
    int arr[] = {10, 80, 30, 90, 40, 50, 70};
    int n = sizeof(arr) / sizeof(arr[0]);
    // 初始化随机种子
    srand(time(NULL));
    quickSort(arr, 0, n - 1);
    printf("Sorted array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}
关键点:
- 随机基准:通过 
rand()函数随机选择一个基准,避免了最坏情况的出现。 
3. 三路划分版本(荷兰国旗问题)
在快速排序中,如果数组中存在大量重复元素,经典的快速排序方法可能会导致性能下降。三路划分版本的快速排序可以处理这种情况,它通过将数组划分为三个部分:小于基准、等于基准、大于基准,从而减少了重复元素的处理。
代码实现:
#include <stdio.h>
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
void threeWayPartition(int arr[], int low, int high, int *lt, int *gt) {
    int pivot = arr[low];
    int i = low + 1;
    int j = high;
    *lt = low;
    *gt = high;
    while (i <= *gt) {
        if (arr[i] < pivot) {
            swap(&arr[i], &arr[*lt]);
            (*lt)++;
            i++;
        } else if (arr[i] > pivot) {
            swap(&arr[i], &arr[*gt]);
            (*gt)--;
        } else {
            i++;
        }
    }
}
void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int lt, gt;
        threeWayPartition(arr, low, high, <, >);
        quickSort(arr, low, lt - 1);
        quickSort(arr, gt + 1, high);
    }
}
int main() {
    int arr[] = {10, 80, 30, 90, 40, 50, 70, 40, 40};
    int n = sizeof(arr) / sizeof(arr[0]);
    quickSort(arr, 0, n - 1);
    printf("Sorted array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}
关键点:
- 三路划分:根据与基准元素的关系,将数组划分为三部分:小于基准、等于基准、大于基准,能够有效处理重复元素。
 - 优化:该方法特别适合包含大量重复元素的数组。
 
总结
- 经典快速排序:使用数组的第一个元素作为基准,适合大多数情况。
 - 随机基准快速排序:通过随机选择基准来避免最坏情况,增加算法的平均性能。
 - 三路划分快速排序:通过三路划分的方式处理重复元素,避免了性能下降,适用于有大量重复元素的数组。
 
这些版本的快速排序可以根据具体的场景进行选择,以提高排序效率。
发表回复