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

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

《信息学奥林匹克辞典》是由机械工业出版社出版、中国计算机学会组编的一本介绍全国青少年信息学奥林匹克竞赛大纲的书籍。“辞典立足于 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.xdnf.cn/news/19559.html

相关文章:

  • Java异常处理完全指南:从入门到精通
  • 安装proteus,并实现stm32仿真
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘pydantic’问题
  • 从 ETL 到 ELT 再到 EAI:AI 如何重塑数据处理
  • 小迪安全v2023学习笔记(七十五讲)—— 验证码安全插件识别攻击利用宏命令
  • 设计模式在Java中的应用:从单例模式到工厂模式的全面解析!
  • 计算机网络总览
  • 使用 GLSL 实现真实自然的纹理混合技术详解
  • 【Java实战⑨】Java集合框架实战:List集合深度剖析
  • 【STM32】外部中断(下)
  • 829作业
  • 告别强化学习?GEPA:用“反思性提示词进化”实现超越的新范式
  • SpringMVC的执行流程
  • 阿里云-应用实时监控服务 ARMS
  • 想学怎么写网站怎么办?初学者专用! (HTML+CSS+JS)
  • 微知-Mellanox OFED编译的一些细节?无法编译怎么办?如何添加自定义编译选项?
  • selenium 元素操作
  • mysql5.7.44安装遇到登录权限问题
  • NM:微生物组数据分析的规划与描述
  • 数字世界的两面性:从乘积组合到最大公约数的算法之旅
  • MCP(Model Context Protocol,模型上下文协议)介绍
  • 计算机毕设选题:基于Python+Django实现电商评论情感分析系统
  • 如何利用AI IDE快速构建一个简易留言板系统
  • 基于SpringBoot + Vue 的宠物领养管理系统
  • Decoder 解码器
  • JPEG XS概述
  • 【51单片机】【protues仿真】基于51单片机智能晾衣架系统
  • centos7安装jdk17
  • Linux 中进入 root 权限
  • C++ 数据结构之哈希表及其相关容器