
本文深入剖析了j*ascript归并排序(merge sort)实现中常见的索引处理、数组复制及边界条件错误,并提供了详细的修正方案和优化建议。通过对比错误代码与优化后的实现,重点阐述了如何采用“左闭右开”区间约定、高效的位运算以及精简的合并逻辑,以构建一个健壮、高效且符合j*ascript编程习惯的归并排序算法。
归并排序是一种高效、稳定的排序算法,它采用分治法(Divide and Conquer)策略。其基本思想是将一个大数组递归地分解为两个子数组,直到子数组只包含一个元素(或为空),然后将这些子数组两两合并,每次合并都确保子数组有序,最终得到一个完全有序的数组。
归并排序主要包含两个核心函数:
在实现归并排序时,开发者常因索引处理不当、边界条件模糊或效率考量不足而引入错误。以下是对一段典型错误代码的详细分析:
function mergesort(arr, left, right) {
if (left < right) {
let mid = parseInt((right - left) / 2) + left; // 问题1:效率较低的中间值计算
mergesort(arr, left, mid);
mergesort(arr, mid + 1, right); // 问题2:与后续merge函数的区间定义可能不一致
merge(arr, left, mid, right);
}
}
function merge(arr, left, mid, right) {
let i = left, j = mid + 1, k = 0, temp = [];
// 合并两个子数组到temp
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k] = arr[i];
i++;
k++;
} else {
temp[k] = arr[j];
j++;
k++;
}
}
// 复制剩余元素(如果存在)
for (; i <= mid; i++) { // 问题3:此循环在特定情况下是多余的
temp[k] = arr[i];
k++;
}
for (; j <= right; j++) { // 问题3:此循环在特定情况下是多余的
temp[k] = arr[j];
k++;
}
// 将temp数组复制回原数组arr
for (let i = left; i <= right; i++) { // 核心问题:索引错误
arr[i] = temp[i]; // 问题4:temp数组的索引k是从0开始,而arr的索引i是从left开始
}
}
let arr = [ 5, 3, 7, 2, 9, 12, 4 ];
let n = arr.length;
mergesort(arr, 0, n); // 问题5:初始调用时right参数的语义不一致
console.log(arr); // 输出: [undefined, undefined, undefined, undefined, undefined, undefined, 3, 5]merge 函数中的数组回写错误 (问题4): 这是导致输出结果出现 undefined 的主要原因。在 merge 函数的最后一个循环中,目的是将 temp 数组中的有序元素复制回 arr 数组的 [left, right] 区间。temp 数组的元素是从索引 0 开始填充的,而 arr 数组的目标区间是从 left 开始的。 错误的写法 arr[i] = temp[i] 导致:
修正方案:
for (let idx = 0; idx < k; idx++) {
arr[left + idx] = temp[idx];
}这里,idx 用于遍历 temp 数组,而 left + idx 则对应 arr 数组中正确的起始位置。
LALAL.AI
AI人声去除器和声乐提取工具
196
查看详情
mergesort 初始调用时的 right 参数语义不一致 (问题5): 在原始代码中,mergesort(arr, left, right) 函数内部的 if (left 最后一个元素的索引(即 [left, right] 闭区间)。 然而,初始调用 mergesort(arr, 0, n) 中,n 是数组的长度 arr.length,这通常表示区间的结束位置(不包含),即 [0, n) 左闭右开区间。这种不一致导致当 arr.length 传递给 right 时,实际处理的数组范围可能超出预期,或者在某些边界条件下出错。
最佳实践:采用统一的“左闭右开”区间约定 [left, right),其中 right 表示区间的结束位置(不包含)。这在许多编程语言和标准库中是常见的做法,可以简化边界条件的处理。
中间值 mid 的计算 (问题1): 原始代码 let mid = parseInt((right - left) / 2) + left; 包含了字符串转换和解析,效率较低。更高效且推荐的做法是使用位运算: let mid = left + ((right - left) >> 1);>> 1 等同于向下取整的除以2,且性能更优。
merge 函数中的冗余循环 (问题3): 在 merge 函数中,当一个子数组的元素全部被复制到 temp 后,另一个子数组中剩余的元素可以直接复制过去。 for (; i
考虑到上述问题和优化点,以下是采用“左闭右开”区间 [left, right) 约定实现的归并排序:
/**
* 归并排序函数
* @param {Array} arr 待排序数组
* @param {number} left 区间起始索引(包含)
* @param {number} right 区间结束索引(不包含)
*/
function mergesort(arr, left, right) {
// 当区间长度大于1时才需要排序
if (right - left > 1) {
// 计算中间索引,采用位运算,等同于 (left + right) / 2 并向下取整
// 避免 (left + right) 溢出,同时保证在 left 和 right 之间
let mid = left + ((right - left) >> 1);
// 递归排序左半部分 [left, mid)
mergesort(arr, left, mid);
// 递归排序右半部分 [mid, right)
mergesort(arr, mid, right);
// 合并两个有序子数组
merge(arr, left, mid, right);
}
}
/**
* 合并两个有序子数组
* @param {Array} arr 原始数组
* @param {number} left 第一个子数组的起始索引(包含)
* @param {number} mid 第一个子数组的结束索引(不包含),也是第二个子数组的起始索引(包含)
* @param {number} right 第二个子数组的结束索引(不包含)
*/
function merge(arr, left, mid, right) {
let i = left; // 左子数组的当前索引 [left, mid)
let j = mid; // 右子数组的当前索引 [mid, right)
let k = 0; // 临时数组的当前索引
let temp = []; // 临时存储合并结果的数组
// 比较两个子数组的元素,将较小的放入temp
while (i < mid && j < right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++]; // 放入temp并递增索引
} else {
temp[k++] = arr[j++]; // 放入temp并递增索引
}
}
// 将左子数组中剩余的元素复制到temp (如果存在)
while (i < mid) {
temp[k++] = arr[i++];
}
// 将右子数组中剩余的元素复制到temp (如果存在)
// 注意:如果左子数组已处理完,这部分才可能执行
// 实际上,这两个while循环只有一个会真正执行,因为另一个子数组已经处理完毕
// 或者两者都执行,直到其中一个子数组耗尽
// 采用 [left, right) 约定,此处的 j < right 是正确的
// 并且不再需要额外的循环来处理剩余部分,因为上面的while循环已经处理了所有情况
// 实际上,第二个 while 循环 (j < right) 在这种写法下是多余的,因为如果 i < mid 结束,j < right 必然成立,且 j 已经移动到正确位置
// 但为了清晰,保留一个处理 j 的循环,虽然在实际运行时,如果 i 已经走完,j 对应的元素会直接被复制。
while (j < right) { // 修正:此循环是必要的,确保右侧剩余元素也被复制
temp[k++] = arr[j++];
}
// 将temp数组中的有序元素复制回原数组arr的对应区间
for (let idx = 0; idx < k; idx++) {
arr[left + idx] = temp[idx];
}
}
// 示例用法
let arr = [ 5, 3, 7, 2, 9, 12, 4 ];
mergesort(arr, 0, arr.length); // 初始调用,right参数为数组长度,符合左闭右开约定
console.log(arr); // 输出: [2, 3, 4, 5, 7, 9, 12]对 merge 函数中剩余元素处理的进一步说明: 在上述优化后的 merge 函数中,while (i
通过理解和应用这些原则,开发者可以编写出高效、正确且易于维护的归并排序实现。
以上就是J*aScript归并排序实现中的常见错误与优化实践的详细内容,更多请关注其它相关文章!
# 写到
# 博客seo建议
# 辉县推广网站搭建好处
# 太阳宫网络营销推广网站
# 东营市建设局网站
# 黄陂seo排名技术
# 泉州洛江网站建设
# 广州seo软件首推乐云seo下拉招代理
# 房山什么是网站优化
# 批发行业自媒体推广营销
# 如何做网站推广工作内容
# 服务端
# 源代码
# javascript
# 较低
# 有什么
# 不包含
# 是从
# 组中
# 递归
# 标准库
# javascript编程
# 优化实践
# 排序算法
# 编程语言
# java
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
2025SNH48年度青春盛典门票价格及购买方式
电脑开不了机怎么办 电脑无法开机的解决方法
《崩坏:星穹铁道》3.6版本异相仲裁打法及配队推荐
猫眼电影app如何筛选支持退改签的影院_猫眼电影退改签影院筛选方法
《蓝色星原:旅谣》坐骑获取攻略
如何快速去除厨房重油污? 2025年最好用的厨房清洁剂推荐
《顺丰同城骑士》查看我的技能方法
漫蛙漫画直连入口 _ manwa官方备用入口实时检测
KFC邀请码怎么使用领额外优惠_KFC邀请码输入方式与额外优惠代码获取方法
铁路12306座位怎么选_12306官方选座操作方法
163邮箱网页版入口 163邮箱在线使用
Win11怎么开启HDR_Windows 11显示器画质增强设置
PHP中获取HTTP响应状态消息:方法与限制
解决Pandas DataFrame高度碎片化警告:高效创建多列的策略
sublime如何配置PHP开发环境_在sublime中运行与调试PHP代码
Teambition网盘如何共享文件
《杖剑传说》食谱大全
《淘票票》添加到苹果钱包教程
餐馆菜篮选购指南
在PHP环境中正确加载HTML资源:CSS样式与图片路径指南
OPPO A3 WiFi频繁断开怎么办 OPPO A3网络优化技巧
Golang如何使用crypto/md5生成哈希_Golang MD5哈希生成方法
教资成绩怎么查询
Fedora怎么安装 Fedora Workstation安装步骤
抖音官网入口快速访问 抖音网页版账号注册解析
漫蛙manwa2网页版书签同步链接_漫蛙manwa多设备登录入口
行者app怎样导出日志
《万兴喵影》导出视频方法
在Django单元测试中优雅处理信号:基于环境的条件执行策略
《雷电模拟器》截图方法介绍
招商淘客入门指南
iPhone16Plus参数配置如何调整声音_iPhone16Plus参数配置声音调整详细方法
《战地6》反作弊已成功拦截240万次作弊 发售第一周98%比赛没有作弊
批改网网页版登录 批改网电脑版学生登录入口
2025考研成绩查询时间入口分享
WooCommerce 新客户订单自动添加管理员备注教程
《盗墓笔记手游》技能介绍
照片整理的黄金法则是怎样的? 理解“收集-筛选-归档-备份”四步流程
DeepSeek超全面指南:入门必看
C++ switch case字符串_C++如何实现字符串switch匹配
《花瓣》创建专辑方法
汽水音乐官网网页版入口 汽水音乐官网网页版在线入口
VBA Outlook邮件自动化:高效集成Excel数据与列标题的策略
win11讲述人怎么关闭 Win11屏幕朗读辅助功能禁用方法【技巧】
CodeIgniter 3 连接 SQL Server:正确获取查询结果的教程
邦丰播放器频道搜索设置
微星主板BIOS怎么调整内存时序_内存参数手动优化BIOS设置教程
我居然低估了 DeepSeek,这次更新它做到了这些!
Go App Engine 项目结构与包管理深度指南
苹果官网国补入口在哪
2025-11-11
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。