C:快速排序程序

代码功能总结

这段代码实现了随机化快速排序算法,并通过以下步骤验证其功能:

  1. 随机数生成 :使用srand(time(0))初始化随机数种子,生成 100 个范围在 0~999 之间的随机整数。
  2. 排序过程
    • 基准选择:每次递归随机选择一个元素作为基准(pivot),减少最坏情况发生的概率。
    • 分区操作 :通过partition函数将数组分为两部分,左侧元素≤基准,右侧元素≥基准。
    • 递归排序:对左右两部分分别递归调用快速排序。
  3. 性能评估 :使用clock()函数测量排序耗时,并输出结果。
  4. 优化处理:当数组长度超过 50 时,自动省略原始数据和排序结果的完整打印,避免输出过多信息。

核心算法分析

1. 随机化基准选择
cpp 复制代码
int random = low + rand() % (high - low + 1);
swap(&arr[random], &arr[high]);
  • 作用:通过随机选择基准元素,避免在已排序或接近排序的数据上出现最坏情况(时间复杂度从 O (n²) 降至 O (n log n))。
2. 分区函数(Partition)
cpp 复制代码
int pivot = arr[high];
int i = (low - 1);

for (int j = low; j <= high - 1; j++) {
    if (arr[j] <= pivot) {
        i++;
        swap(&arr[i], &arr[j]);
    }
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
  • 原理:将基准元素放到正确位置,使左侧元素≤基准,右侧元素≥基准。
  • 复杂度:时间复杂度 O (n),空间复杂度 O (1)。
3. 递归排序
cpp 复制代码
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
  • 分治思想:不断将数组划分为更小的子数组,直到子数组长度为 1 或 0。

性能分析

  1. 时间复杂度
    • 平均情况:O (n log n)(随机化基准有效避免最坏情况)。
    • 最坏情况:O (n²)(理论上仍可能发生,但概率极低)。
  2. 空间复杂度:O (log n)(递归调用栈的深度)。
  3. 稳定性:不稳定(元素交换可能改变相同值元素的相对顺序)。

代码优点

  1. 随机化优化:通过随机选择基准,显著提高了算法在各种数据分布下的性能。
  2. 计时功能 :使用clock()函数直观展示排序耗时,便于性能对比。
  3. 用户体验优化:自动判断数组长度,避免打印过长数据。
  4. 模块化设计:函数职责清晰(交换、分区、排序、打印),便于维护和复用。

潜在改进点

  1. 小规模数据优化

    • 当子数组长度较小时(如≤16),使用插入排序(时间复杂度 O (n²),但常数项更小)可能更快。
  2. 三路划分(Three-Way Partitioning)

    • 对于包含大量重复元素的数据,使用三路划分将数组分为<=>三部分,可减少递归次数。
  3. 尾递归优化

    • 对较大的子数组先进行递归,较小的子数组使用循环,降低栈空间消耗。
  4. 三数取中法(Median of Three)

    • 选择首、中、尾三个元素的中间值作为基准,进一步提高划分平衡性。

适用场景

  • 大规模数据排序:快速排序的平均性能优于冒泡、选择、插入等简单排序算法。
  • 内存受限环境:原地排序(in-place),空间复杂度低。
  • 随机分布数据:随机化基准确保算法在大多数情况下表现良好。

总结

这段代码实现了一个功能完整、性能优良的随机化快速排序算法,通过随机基准选择有效避免了最坏情况。代码结构清晰,包含必要的输入生成、排序过程和结果验证,适合作为教学示例或基础排序工具。对于特定场景(如大量重复元素或小规模数据),可通过进一步优化提升性能。

完整测试代码

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define SIZE 100
#define INSERTION_SORT_THRESHOLD 16

// 交换两个元素的函数
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 插入排序(处理小规模数据)
void insertionSort(int arr[], int low, int high) {
    for (int i = low + 1; i <= high; i++) {
        int key = arr[i];
        int j = i - 1;
        
        // 将比key大的元素后移
        while (j >= low && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

// 三数取中法选择基准
int medianOfThree(int arr[], int low, int high) {
    int mid = low + (high - low) / 2;
    
    // 保证arr[low] <= arr[mid] <= arr[high]
    if (arr[low] > arr[mid])
        swap(&arr[low], &arr[mid]);
    if (arr[low] > arr[high])
        swap(&arr[low], &arr[high]);
    if (arr[mid] > arr[high])
        swap(&arr[mid], &arr[high]);
    
    // 将中间值交换到high-1位置
    swap(&arr[mid], &arr[high-1]);
    return arr[high-1];  // 返回基准值
}

// 三路划分处理重复元素
void quickSort3Way(int arr[], int low, int high) {
    if (low >= high) return;
    
    // 小规模数据使用插入排序
    if (high - low + 1 <= INSERTION_SORT_THRESHOLD) {
        insertionSort(arr, low, high);
        return;
    }
    
    // 三数取中法选择基准
    int pivot = medianOfThree(arr, low, high);
    
    // 三路划分:[low, lt-1] < pivot, [lt, gt] == pivot, [gt+1, high] > pivot
    int lt = low;      // 小于区域的右边界
    int gt = high;     // 大于区域的左边界
    int i = low;       // 当前扫描位置
    
    while (i <= gt) {
        if (arr[i] < pivot) {
            swap(&arr[lt], &arr[i]);
            lt++;
            i++;
        } else if (arr[i] > pivot) {
            swap(&arr[i], &arr[gt]);
            gt--;
        } else {  // arr[i] == pivot
            i++;
        }
    }
    
    // 尾递归优化:只递归处理较小的部分
    if (low < lt - 1)
        quickSort3Way(arr, low, lt - 1);
    if (gt + 1 < high)
        quickSort3Way(arr, gt + 1, high);
}

// 打印数组函数
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++)
        printf("%d ", arr[i]);
    printf("\n");
}

// 主函数
int main() 
{
    int arr[SIZE];
    
    // 设置随机数种子(基于当前时间)
    srand(time(0));
    
    // 生成100个随机数(范围:0~999)
    for (int i = 0; i < SIZE; i++) {
        arr[i] = rand() % 1000; // 0~999
    }

    int n = sizeof(arr) / sizeof(arr[0]);
    
    printf("原数组: \n");
    if (n <= 50) { // 当数组不太长时才打印,避免刷屏
        printArray(arr, n);
    } else {
        printf("数组过大,省略打印...\n");
    }
    
    // 计时开始
    clock_t start, end;
    double cpu_time_used;
    start = clock();
    
    // 使用优化后的三路快速排序
    quickSort3Way(arr, 0, n - 1);
    
    // 计时结束
    end = clock();
    cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
    
    printf("排序后的数组: \n");
    if (n <= 50) { // 当数组不太长时才打印,避免刷屏
        printArray(arr, n);
    } else {
        printf("数组过大,省略打印...\n");
    }
    
    printf("排序耗时: %f 秒\n", cpu_time_used);
    
    return 0;
}
相关推荐
智者知已应修善业17 分钟前
【51单片机2位数码管100毫秒的9.9秒表】2022-5-16
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
卖猪肉的痴汉24 分钟前
5.2 Qt Creator 使用FFmpeg库
开发语言·qt·ffmpeg
teeeeeeemo1 小时前
Number.toFixed() 与 Math.round() 深度对比解析
开发语言·前端·javascript·笔记
我在北京coding1 小时前
Uncaught (in promise) TypeError: x.isoWeek is not a function
开发语言·javascript·vue.js
showmethetime1 小时前
[设计模式]创建型模式-单例模式
开发语言
Y1_again_0_again1 小时前
Java 包装类详解
java·开发语言
CC大煊2 小时前
【java】@RestController和@Controller的区别
java·开发语言
Ven%2 小时前
掌握Bash脚本编写:从服务启动脚本到语法精要
linux·服务器·开发语言·chrome·bash
安全系统学习2 小时前
【网络安全】文件上传型XSS攻击解析
开发语言·python·算法·安全·web安全