当前位置: 首页 > 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.lryc.cn/news/600750.html

相关文章:

  • 安卓服务与多线程
  • 学习嵌入式的第三十天-数据结构-(2025.7.21)网络编程
  • 系统性学习C语言-第二十三讲-文件操作
  • 台式电脑有多个风扇开机只有部分转动的原因
  • Matlab自学笔记六十五:解方程的数值解法(代码速成)
  • Nacos-服务注册,服务发现(二)
  • 八股文整理——计算机网络
  • 容器化成本优化:K8s资源请求与限制的黄金法则——从资源画像分析到25%成本削减的实战指南
  • 记录和分享抓取的数字货币和大A时序数据
  • 什么是ICMP报文?有什么用?
  • Matlab学习笔记:自定义函数
  • java基础(day16)set-map
  • DAY24 元组和OS模块
  • 【安全漏洞】网络守门员:深入理解与应用iptables,守护Linux服务器安全
  • Java基础-文件操作
  • spring Could 高频面试题
  • 面试问题总结——关于OpenCV(二)
  • 详解力扣高频SQL50题之619. 只出现一次的最大数字【简单】
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——6. 传统算法实战:用OpenCV测量螺丝尺寸
  • 人工智能之数学基础:概率论之韦恩图的应用
  • Java 镜像减肥记:Docker 多阶段构建全攻略
  • 统计学08:概率分布
  • 【SSM】第二章 网上蛋糕项目商城-首页
  • lottie 动画使用
  • MySQL数据库本地迁移到云端完整教程
  • 《每日AI-人工智能-编程日报》--2025年7月26日
  • 使用Netty搭建一个网络聊天室
  • Java面试题及详细答案120道之(041-060)
  • 图片查重从设计到实现(5)Milvus可视化工具
  • 力扣872. 叶子相似的树