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

《信息学奥林匹克辞典》中的一个谬误

《信息学奥林匹克辞典》是由机械工业出版社出版、中国计算机学会组编的一本介绍全国青少年信息学奥林匹克竞赛大纲的书籍。“辞典立足于 NOI 大纲的知识体系,从准确性、学术性和实用性等原则出发,对有关的知识和概念给出了严谨的解析,并在此基础上对所涉及的思想、方法和技巧做了精要的述评,全面涵盖了全国青少年信息学奥林匹克竞赛所考查的计算机科学基础知识、程序设计语言及其环境、数据结构与算法,以及数学和其他内容。“(以上内容引用自此书的前勒口)

这本书有370页,内容覆盖了 CSP-J/S 和 NOI 竞赛大纲。笔者在撰写《CCF GESP 直通车》时,就引用了书中的部分内容。应该说,书中的绝大部分内容都是正确的,但百密一疏,笔者发现了2.4.4.1归并排序中的一个小错误。笔者觉得这个错误不是一般的错误,读者未必能看得出来,所以有必要指出来。但是出版社没有留下任何的联系方式(虽然有几个客服电话,但是笔者觉得这样的错误客服未必能理解并准确地传递给相关人员),于是撰文指出,目的不是为了批评出版社和作者,而是为了指出问题,让其他读者收益,并希望在后续重印时能够修正。

这个错误是第175页,归并排序代码的最后一行(第175页的最后一行)。为了让没有这本书的读者也能理解这个问题,下面贴出代码(第175页的最后一行对应与这里的第17行):

//函数 mergeSort可完成数组中指定范围内的归并排序
//a:为待排序元素的数组
//b:存放中间结果的临时数组
//l:数组a中待排序元素的最小下标 
//r:数组a中待排序元素的最大下标 
void mergeSort(int *a, int l, int r) {if(r == l)					//当前元素数量为1,无需处理 return;int mid = (l + r) >> 1;mergeSort(a, l, mid);		//递归调用,对左半部分元素进行排序 mergeSort(a, mid + 1, r);	//递归调用,对右半部分元素进行排序int i = l, j = mid + 1, cnt = l - 1;//合并排好序的左右两部分 while (i <= mid && j <= r) {if (a[i] < a[j])b[++cnt] = a[i++];elseb[++cnt] = a[j++];}while (i <= mid)b[++cnt] = a[i++];while (j <= r)b[++cnt] = a[j++];//将排好序的元素依次复制到原数组中 for (int i = l; i <= r; ++i)a[i] = b[i];
}

这里的第17行为“if (a[i] < a[j])”,正确的写法应该是“if (a[i] <= a[j])”。

虽然只差了一个字符,但是前者会导致排序不稳定,后者才是稳定的。

我们知道稳定性是衡量排序好坏的一个重要的指标。归并排序一般认为是稳定的(书中未提及到,这也是再印时需要补充的),但是前提是,代码必须要写对,才能保证排序稳定。书中的代码,当a[i] = a[j] 时,会先选择a[j],而 a[j] 是序列中右半部分的元素,这就会导致相等的元素中,原来在右侧的现在到了左侧,破坏了稳定性。

当然,更好的写法是:

if (a[j] < a[i]) b[++cnt] = a[j++];
elseb[++cnt] = a[i++];

即仍然使用“<”,但把 a[j] 放在左边。这样做的好处是,跟 compare 函数的逻辑一致。我们知道,在 C++ 中,许多排序函数都可以带一个自定义的比较函数。在这个比较函数中,第一个参数位于原序列中的右边,第二个参数位于原序列中的左边。compar 函数的写法一般是这样(假设比较的两个数为 int 类型,排序的要求是升序):

bool compare(int &a, int &b) {return (a < b);
}

这里用的就是“<”。

如果在合并时需要使用 compare 函数,则一般是这样:

if (compare(a[j], a[i]))b[++cnt] = a[j++];
elseb[++cnt] = a[i++];

对此,大家有什么看法呢?欢迎留言讨论。

本文为学漄乐码堂主撰写。要了解学漄乐码学堂更多的信息,可查看堂主个人简介。

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

相关文章:

  • 【Android】Notification 的基本使用
  • C++ 快速复习指南(上半部分)
  • 房屋租赁系统|基于SpringBoot和Vue的房屋租赁系统(源码+数据库+文档)
  • 8.31【Q】CXL-DMSim:
  • 数学分析原理答案——第七章 习题12
  • 设置Ubuntu 22.04 LTS上的rsync同步服务
  • Hysplit大气传输和污染扩散-轨迹聚合标准20%30%用途
  • 国内大型银行数据模型实践案例
  • PerfectSquares.java
  • 解决COB支架偏移载具分析问题根源
  • 编译器领域 名人堂
  • 单表查询-group by rollup优化
  • ibping基本使用 以及 包丢失 超时 排障
  • 【GPT入门】第62课 情感对话场景模型选型、训练与评测方法,整体架构设计
  • Redis核心原理与Java应用实践
  • 基于Seurat的空转单样本数据分析流程学习(一)
  • 性能优化三剑客:`memo`, `useCallback`, `useMemo` 详解
  • 7.1elementplus的表单
  • PyQt5 K线图实现与性能优化详解
  • 我的创作纪念日|一周年,上海-昆明-上海
  • QoS 基本原理与配置
  • 学生请假就餐系统
  • AI 赋能 Java 开发效率:全流程痛点解决与实践案例(二)
  • 使用cursor claude sonnet4的一些感受
  • 【系统分析师】高分论文:论原型法及其在系统开发中的应用
  • Langflow 框架中 Prompt 技术底层实现分析
  • LangChain中Prompt处理机制的技术架构与核心思想分析
  • [信号与系统个人笔记]第三章 连续时间信号与系统的频域分析
  • PPO、DPO和GRPO的区别
  • 【百度】C++开发(25届提前批 一面)面经