当前位置: 首页 > news >正文

深入理解C语言快速排序与自省排序(Introsort)

排序算法是计算机科学中最基础也是最重要的知识之一。快速排序(Quicksort)因其平均时间复杂度为 O(n log n) 而广受欢迎,但在最坏情况下会退化到 O(n²)。为了克服这一缺点,自省排序(Introsort) 应运而生,它结合了快速排序、堆排序和插入排序的优点,成为C++标准库(std::sort)的默认实现。

本文将详细介绍:

  1. 快速排序的原理与优化

  2. 自省排序的设计思想

  3. C语言实现自省排序

  4. 性能分析与对比


1. 快速排序(Quicksort)回顾

快速排序由Tony Hoare于1959年提出,采用 分治法(Divide and Conquer)

  1. 选择基准值(Pivot):通常取第一个、最后一个或随机元素。

  2. 分区(Partition):将数组分为两部分,左边 ≤ pivot,右边 ≥ pivot。

  3. 递归排序:对左右子数组重复上述过程。

C语言实现(经典快速排序)

c

void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}int partition(int arr[], int low, int high) {int pivot = arr[high]; // 选择最后一个元素作为基准int i = low - 1;       // i 指向小于pivot的区域的末尾for (int j = low; j < high; j++) {if (arr[j] <= pivot) {i++;swap(&arr[i], &arr[j]);}}swap(&arr[i + 1], &arr[high]); // 将pivot放到正确位置return i + 1;
}void quickSort(int arr[], int low, int high) {if (low < high) {int pi = partition(arr, low, high);quickSort(arr, low, pi - 1);quickSort(arr, pi + 1, high);}
}

快速排序的缺点

  • 最坏情况(如数组已有序或逆序):递归深度 O(n),时间复杂度 O(n²)

  • 对小数组效率低:递归调用开销大。


2. 自省排序(Introsort)

自省排序由David Musser于1997年提出,结合了:

  1. 快速排序(主算法,高效分区)

  2. 堆排序(防止最坏情况)

  3. 插入排序(优化小数组)

核心思想

  1. 递归深度限制

    • 如果递归深度超过 2 log₂n,切换到堆排序(保证最坏情况 O(n log n))。

  2. 小数组优化

    • 当子数组大小 ≤ 16(经验值),改用插入排序(减少递归开销)。

  3. 三数取中法(Median-of-Three)

    • 优化基准值选择,减少最坏情况概率。


3. C语言实现自省排序

c

#include <stdio.h>
#include <stdlib.h>
#include <math.h>// 交换两个元素
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;while (j >= low && arr[j] > key) {arr[j + 1] = arr[j];j--;}arr[j + 1] = key;}
}// 堆排序(用于防止快速排序退化)
void heapify(int arr[], int n, int i) {int largest = i;int left = 2 * i + 1;int right = 2 * i + 2;if (left < n && arr[left] > arr[largest])largest = left;if (right < n && arr[right] > arr[largest])largest = right;if (largest != i) {swap(&arr[i], &arr[largest]);heapify(arr, n, largest);}
}void heapSort(int arr[], int low, int high) {int n = high - low + 1;for (int i = n / 2 - 1; i >= 0; i--)heapify(arr, n, i);for (int i = n - 1; i > 0; i--) {swap(&arr[0], &arr[i]);heapify(arr, i, 0);}
}// 三数取中法选择基准值
int medianOfThree(int arr[], int low, int high) {int mid = low + (high - low) / 2;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]);return mid; // 返回中间值的索引
}// 自省排序核心
void introSortUtil(int arr[], int low, int high, int depthLimit) {int size = high - low + 1;// 小数组优化:改用插入排序if (size <= 16) {insertionSort(arr, low, high);return;}// 递归深度超限:改用堆排序if (depthLimit == 0) {heapSort(arr, low, high);return;}// 三数取中法选择基准值int pivotIdx = medianOfThree(arr, low, high);swap(&arr[pivotIdx], &arr[high]);// 快速排序分区int pi = partition(arr, low, high);// 递归排序左右子数组introSortUtil(arr, low, pi - 1, depthLimit - 1);introSortUtil(arr, pi + 1, high, depthLimit - 1);
}// 自省排序入口
void introSort(int arr[], int n) {int depthLimit = 2 * log2(n); // 计算最大递归深度introSortUtil(arr, 0, n - 1, depthLimit);
}

4. 性能对比

算法最佳平均最坏适用场景
快速排序O(n log n)O(n log n)O(n²)通用排序
堆排序O(n log n)O(n log n)O(n log n)最坏情况保证
插入排序O(n)O(n²)O(n²)小数组优化
自省排序O(n log n)O(n log n)O(n log n)综合最优

5. 总结

  • 自省排序 = 快速排序 + 堆排序 + 插入排序,综合了三种算法的优势。

  • 防止快速排序退化:递归深度超限时切换堆排序。

  • 优化小数组:改用插入排序减少递归开销。

  • C++ STL的std::sort就是基于自省排序,适用于绝大多数场景。

适用场景

  • 大规模数据排序(如数据库、科学计算)。

  • 需要稳定高效排序(避免最坏情况)。

http://www.dtcms.com/a/300154.html

相关文章:

  • 【每天一个知识点】GAN(生成对抗网络,Generative Adversarial Network)
  • Compose笔记(三十八)--CompositionLocal
  • 安卓学习记录1——持续更新ing
  • React组件中的this指向问题
  • 三防平板支持DMR对讲有什么用?实现高效集群调度
  • 如何理解“测试场景”与“测试要点”的区别和联系?
  • Linux系统架构核心全景详解
  • 从0到1学Pandas(六):Pandas 与数据库交互
  • KiCad 与 CircuitMaker 使用方法分享:从零开始学电子设计
  • JavaWeb(苍穹外卖)--学习笔记11(Filter(过滤器) 和 Interceptor(拦截器))
  • Windows开发,制作开发软件安装程序(一)
  • MySQL的底层原理--InnoDB数据页结构
  • 关于GateWay网关
  • 基于HMM的词性标注方法详解(HMM+Viterbi,例题分析)
  • 【专业扫盲】电压/电流反馈和串联/并联反馈
  • CSP2025模拟赛2(2025.7.26)
  • 机器人仿真(2)Ubuntu24.04下RTX5090配置IsaacSim与IsaacLab
  • Jenkins持续集成工具
  • swagger基本注解@Tag、@Operation、@Parameters、@Parameter、@ApiResponse、@Schema
  • (1-7-4) MySql 的高级查询
  • 20250726-2-Kubernetes 网络-Service 定义与创建_笔记
  • 【Spring Cloud】微服务学习
  • 超时进行报警例子
  • 在 Windows 系统中实现 WinToGo 的 VHDX 文件切换使用的常见方法
  • 什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?
  • Spring AI Alibaba Video 示例
  • 大型微服务项目:听书——12 数据一致性自定义starter封装缓存操作
  • Java设计模式之行为型模式(中介者模式)实现方式与测试方法
  • 大数据之路:阿里巴巴大数据实践——实时技术与数据服务
  • Reeden:跨平台 AI 电子书阅读器