新闻详情

新闻详情

首页 / 资讯中心 / 详情

经典内省排序 Introsort —— David R. Musser, 1997

发布时间:2026/10/1 7:23:13来源:尧图网络
经典内省排序 Introsort —— David R. Musser, 1997
// // 经典内省排序 Introsort —— David R. Musser, 1997// 论文:Introspective Sorting and Selection Algorithms,// Software: Practice and Experience, 27(8):983-993, 1997.// 单文件完整版,含 main 用例。编译运行:// g -stdc17 -Wall -Wextra main.cpp -o main ./main// 预期输出:all tests passed// // 算法三组件(1997 原始结构,无 std::sort 增强项):// 1. 快速排序 —— 主流路径,median-of-three 选枢轴 单向分区;// 2. 堆排序(自实现) —— 递归深度超过 2*lg(n) 时整段切换,// 把最坏复杂度锁死在 O(n log n);// 3. 插入排序 —— 区间长度 16 时收尾(小数组上常数极小)。// 深度计数是「内省」的本体:快排每层递归把区间减半,理想深度 lg(n),// 超过 2 倍余量即判定输入病态(有序/逆序/大量重复),立刻降级堆排。//// 命名注意:命名空间内的非限定调用(如 partition/introsort)可能被 ADL// (实参关联命名空间含 std 时把 std::partition 等拉进候选集)引入歧义;// 凡与 std 同名的内部函数,调用点一律写全限定 classic_introsort::xxx。// #includealgorithm#includecassert#includecstdio#includefunctional#includeiterator#includerandom#includeutility#includevectornamespaceclassic_introsort{// 阈值:区间长度不超过 16 时,插入排序比快排便宜staticconstexprstd::ptrdiff_t kThreshold16;// ---------------------------------------------------------------// 辅助 1:log2 下取整(用于计算递归深度上限)// ---------------------------------------------------------------// 返回最大的 k,使 2^k n,即 n 的二进制最高位序号。// 深度上限取 2 * lg2(n):理想平衡快排递归深度恰为 lg(n),// 乘 2 等于给快排两倍「犯错余量」,超过即判定病态。std::ptrdiff_tlg2(std::ptrdiff_t n){std::ptrdiff_t k0;for(;n!0;n1)k;// 反复右移直至归零,统计位数returnk-1;// 最高位序号 位数 - 1}// ---------------------------------------------------------------// 组件 1:插入排序(对小区间收尾)// ---------------------------------------------------------------// 稳定、原地、对近乎有序序列接近 O(n)。// 从第 2 个元素起,每个元素向前找到正确位置并插入// (比它大的元素整体右移一格,key 落位)。// 必须守卫空区间:first last 时 first1 已越界,// 直接解引用 *i 会段错误(经典版本遗漏点,已补)。templatetypenameRandomIt,typenameComparevoidinsertion_sort(RandomIt first,RandomIt last,Compare comp){if(firstlast)return;// 空区间守卫(修复点)for(RandomIt ifirst1;i!last;i){typenamestd::iterator_traitsRandomIt::value_type keystd::move(*i);// 取出待插值(值拷贝,基准恒定)RandomIt ji;while(j!firstcomp(key,*(j-1))){*jstd::move(*(j-1));// 比 key 大的逐个右移--j;}*jstd::move(key);// key 落到正确位置}}// ---------------------------------------------------------------// 组件 2:堆排序(自实现,深度超限时的兜底)// ---------------------------------------------------------------// 二叉最大堆(按 comp 比较)下沉操作:// root 与其左右子(2*root1 / 2*root2)中较大者比较,// 若不满足堆序则交换并继续下沉,保证子树恢复堆性质。templatetypenameRandomIt,typenameComparevoidsift_down(RandomIt first,std::ptrdiff_t n,std::ptrdiff_t root,Compare comp){while(true){std::ptrdiff_t child2*root1;// 左子下标if(childn)return;// 越界 - 已是叶子,下沉完成if(child1ncomp(first[child],first[child1])){child;// 取左右子中「较大」者}if(!comp(first[root],first[child]))return;// 父已不小于子,完成std::iter_swap(firstroot,firstchild);// 父子交换rootchild;// 继续向下调整}}// 堆排序:先建堆(自底向上逐结点下沉),再反复把堆顶// (全局最大)换到末尾,堆规模减一后重新下沉堆顶。// 最坏 O(n log n),空间 O(1)。templatetypenameRandomIt,typenameComparevoidheapsort(RandomIt first,RandomIt last,Compare comp){conststd::ptrdiff_t nlast-first;for(std::ptrdiff_t in/2-1;i0;--i)// 建堆:最后一个非叶起sift_down(first,n,i,comp);for(std::ptrdiff_t in-1;i0;--i){// 逐元素取出堆顶std::iter_swap(first,firsti);// 堆顶(最大)归位末尾sift_down(first,i,0,comp);// 剩余部分重新调整}}// ---------------------------------------------------------------// 辅助 2:三点取中,返回中位数的迭代器(经典选枢轴法)// ---------------------------------------------------------------// 取 first、mid、last-1 三个元素的中位数作枢轴候选:// 避免取到区间最小/最大元素而退化成「倾斜分区」,// 对已有序/逆序输入第一趟就能切到中位。templatetypenameRandomIt,typenameCompareRandomItmedian_of_three(RandomIt a,RandomIt b,RandomIt c,Compare comp){if(comp(*a,*b)){// a bif(comp(*b,*c))returnb;// a b c - 中位 breturncomp(*a,*c)?c:a;// c b,则中位为 c 或 a}else{// b aif(comp(*a,*c))returna;// b a c - 中位 areturncomp(*b,*c)?c:b;// c a,则中位为 c 或 b}}// ---------------------------------------------------------------// 组件 1 核心:单向分区(Lomuto 式)// ---------------------------------------------------------------// 选三点中位作枢轴,换到区间末尾;从左扫描,// 凡 枢轴的元素依次换到前部(store 指针标记边界);// 最后把枢轴放回分界处。// 返回 cut:枢轴最终位置,满足// [first,cut) 全部 枢轴, [cut1,last) 全部 枢轴。// 调用前提:last - first kThreshold 0,故 last-1 恒有效。templatetypenameRandomIt,typenameCompareRandomItpartition(RandomIt first,RandomIt last,Compare comp){RandomIt midfirst(last-first)/2;// 中点RandomIt pivot_itmedian_of_three(first,mid,last-1,comp);std::iter_swap(pivot_it,last-1);// 枢轴挪到末尾typenamestd::iterator_traitsRandomIt::value_type pivotstd::move(*(last-1));// 值拷贝枢轴(基准恒定)RandomIt storefirst;// 小元素放置区边界for(RandomIt itfirst;it!last-1;it){if(comp(*it,pivot)){// 比枢轴小 - 归左区std::iter_swap(it,store);store;}}std::iter_swap(store,last-1);// 枢轴放回分界处returnstore;// 枢轴位置}// ---------------------------------------------------------------// 组件 1 主递归:introsort(经典 1997 结构)// ---------------------------------------------------------------// 循环条件:区间长度 阈值才细分,否则交给插入排序收尾。// 每轮:// 1) 深度耗尽 - 整段切堆排序,该段彻底排好,立即返回;// 2) 消耗一层深度额度;// 3) 分区得到枢轴位置 cut;// 4) 递归处理右半 [cut1, last);// 5) 左半 [first, cut] 交给外层 while 继续迭代(半迭代省栈)。// 与 std 同名的调用一律写全限定,避免 ADL 引入候选歧义。templatetypenameRandomIt,typenameComparevoidintro_sort_impl(RandomIt first,RandomIt last,std::ptrdiff_t depth,Compare comp){while(last-firstkThreshold){// 段还太大,需要继续切if(depth0){// 递归太深 - 病态输入heapsort(first,last,comp);// 整段堆排序兜底return;// 该段已彻底排好}--depth;// 消耗一层递归额度RandomIt cutclassic_introsort::partition(first,last,comp);classic_introsort::intro_sort_impl(cut1,last,depth,comp);// 递归右半lastcut;// 左半(含枢轴)交给下次循环}// 退出 while 时本段 阈值,留待收尾插入排序}// ---------------------------------------------------------------// 统一入口:introsort// ---------------------------------------------------------------// 深度上限 2 * lg2(n):每次进入递归前都会 -1,// 即默认给快排预留「两倍最优深度」的犯错空间。// 流程 intro_sort_impl(先切段) - insertion_sort(收尾)。templatetypenameRandomIt,typenameComparevoidintrosort(RandomIt first,RandomIt last,Compare comp){conststd::ptrdiff_t nlast-first;if(nkThreshold){intro_sort_impl(first,last,2*lg2(n),comp);}insertion_sort(first,last,comp);}// 无比较器重载:默认升序(std::less)templatetypenameRandomItvoidintrosort(RandomIt first,RandomIt last){classic_introsort::introsort(first,last,std::lesstypenamestd::iterator_traitsRandomIt::value_type());}}// namespace classic_introsort// // main:用例覆盖 正常 / 空 / 单元素 / 已有序 / 逆序 / 全重复 /// 大量重复小值域 / 随机对照 / 自定义比较器// intmain(){// 1) 常规乱序std::vectorintv1{5,3,8,1,9,2,7,4,6};classic_introsort::introsort(v1.begin(),v1.end());assert(std::is_sorted(v1.begin(),v1.end()));// 2) 空区间(曾触发段错误:insertion_sort 空区间越界,已修复)std::vectorintempty;classic_introsort::introsort(empty.begin(),empty.end());assert(std::is_sorted(empty.begin(),empty.end()));// 3) 单元素std::vectorintone{42};classic_introsort::introsort(one.begin(),one.end());assert(std::is_sorted(one.begin(),one.end()));// 4) 病态输入 A:已有序(传统快排的最坏情形)std::vectorinta;for(inti0;i4096;i)a.push_back(i);classic_introsort::introsort(a.begin(),a.end());assert(std::is_sorted(a.begin(),a.end()));// 5) 病态输入 B:完全逆序std::vectorintb(a.rbegin(),a.rend());classic_introsort::introsort(b.begin(),b.end());assert(std::is_sorted(b.begin(),b.end()));// 6) 病态输入 C:全部相等(大量重复,考验分区稳定性与死循环防护)std::vectorintc(100000,7);classic_introsort::introsort(c.begin(),c.end());assert(std::is_sorted(c.begin(),c.end()));// 7) 大量重复 小值域(重元素排列的典型压力形态)std::mt19937rng2(7);std::vectorintg(50000),h;for(intx:g)xstatic_castint(rng2()%64);hg;std::sort(h.begin(),h.end());// 基准答案classic_introsort::introsort(g.begin(),g.end());assert(gh);// 8) 随机 10000 个,与 std::sort 结果逐元素对照std::mt19937rng(42);std::vectorintd(10000),e;for(intx:d)xstatic_castint(rng()%100000);ed;std::sort(e.begin(),e.end());// 基准答案classic_introsort::introsort(d.begin(),d.end());assert(de);// 9) 自定义比较器:降序std::vectorintf{1,9,2,8,3,7,0,5};classic_introsort::introsort(f.begin(),f.end(),std::greaterint());assert(std::is_sorted(f.begin(),f.end(),std::greaterint()));std::puts(all tests passed);return0;}
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Linux GLIBC版本兼容性诊断与跨发行版部署指南 2026/10/1 8:29:20

Linux GLIBC版本兼容性诊断与跨发行版部署指南

1. 为什么一个“查版本”的命令,会让运维和开发者半夜爬起来改配置?你有没有遇到过这样的场景:刚把新编译的程序拷到测试服务器上,一执行就报错——./myapp: /lib64/libc.so.6: versionGLIBC_2.28 not found (required by ./myapp…

阅读更多 →
2026年跨境电商数据分析工具对比测评3类主流工具横向评测:新手到成熟卖家的分层选型指南 2026/10/1 8:29:20

2026年跨境电商数据分析工具对比测评3类主流工具横向评测:新手到成熟卖家的分层选型指南

一、先说结论:跨境电商数据分析工具可以分成三类 市面上跨境电商卖家常用的数据分析工具,按功能定位大致可以分成三类:平台原生后台报表(看单店现状)、通用表格工具(以 Excel 为代表,做灵活计算…

阅读更多 →
长文案例分享:Agent 上线用不了,除了模型问题,别忘了检查 harness 2026/10/1 8:29:20

长文案例分享:Agent 上线用不了,除了模型问题,别忘了检查 harness

有了MCP后,企业内部的科技部门有想法,都会选择做企业内部的agent平台,一个入口打通下游多个业务系统,让用户不用频繁切换系统,还可以基于自然语言完成跨系统的任务(比如一句话基于crm里的订单写一封邮件&am…

阅读更多 →
2026年五大私有化经销订货商城推荐:部署与数据控制解析 2026/10/1 8:29:20

2026年五大私有化经销订货商城推荐:部署与数据控制解析

2026年五大私有化经销订货商城推荐:部署与数据控制解析摘要私有化这三个字,最近几年在选型会上被提到的频率明显变高。渠道价格体系、经销商档案、客户成交数据,这些内容过去放在谁的服务器上,很多企业并不太在意;但当…

阅读更多 →
用 SVG 内联动画打造灵动波浪下划线:30-seconds-of-code 的 Squiggle Link Hover Effect 详解 2026/10/1 8:29:07

用 SVG 内联动画打造灵动波浪下划线:30-seconds-of-code 的 Squiggle Link Hover Effect 详解

教程文档 【免费下载链接】30-seconds-of-code Coding articles to level up your development skills 项目地址: https://gitcode.com/gh_mirrors/30/30-seconds-of-code 点击查看 免费下载 链接的 hover 效果是网页交互中最细小却最能体现设计巧思的细节之一。本…

阅读更多 →
学习STM32掉电存储 2026/10/1 8:29:07

学习STM32掉电存储

1.添加基础库 stm 官网下载地址:https://doc.embedfire.com/products/link/zh/latest/mcu/stm32/stm32f103c8t6_core_new1.html 项目参数配置: Preprocessor Symbols Define 设置参数: USE_STDPERIPH_DRIVER,USE_STM3210C_EVAL #需要添加的文…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉