开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 823|回复: 20
收起左侧

[易语言纯源码] 混合归并排序 不固定步长 和龙虾写的

[复制链接]
结帖率:75% (3/4)
发表于 2026-6-13 15:01:17 | 显示全部楼层 |阅读模式   福建省福州市
分享源码
界面截图: -
是否带模块: -
备注说明: -
本帖最后由 APPLEUFO 于 2026-6-13 15:02 编辑

只能用于整数型,想用与 文本型  改改就是了  

原来的归并是固定步长   现在不固定     对完全方向相反,最差情况有奇效 ,可用直接反转数组完事

混合归并排序原理
一句话概括
首趟扫描找自然段边界,后续轮次复用边界做归并,不再重新扫描。

三个阶段
1. 扫描自然段(只做一次,O(n))

从左到右扫一遍数组,遇到 a > a[i+1] 就是一个"降序断点",说明前面的自然升序段结束了。把每个段的起始下标存到 游标首[],结束下标存到 游标尾_[]。

比如 [3,7,12, 5,9, 2, 6,8,15, 1,4,10] 一趟扫完,分段计数 = 5,每个段的边界都记录好了。

2. 快速判断(O(1))

扫描完看分段计数:

分段计数 = 1 → 整个数组已经升序,直接返回
分段计数 = 个数 → 每个元素自成一档,说明完全降序,反转即可
这两个极端场景不用归并,白捡 O(n) 甚至 O(1) 的性能。

3. 多轮归并(核心循环)

每轮做的事:

合并循环:步长为 2 遍历分段,相邻两段调 子程序_合并4 做两路归并。落单的段(奇数个分段时最后那个)传 右首=0, 右尾=0,子程序检测到直接 内存拷贝 复制过去不合并。
边界更新循环:同样步长为 2,游标首[m1] = 游标首[n1](新段头 = 左段头),游标尾_[m1] = 游标尾_[n1+1](新段尾 = 右段尾)。落单的段尾就是它自己的尾。
分段计数变成 (分段计数+1) \ 2(大概减半)
局变_源是临时 取反,切换源/目标数组
4. 双缓冲交替写入

关键优化:不每轮全数组拷贝。用 参数_数组 和 临时_归并 两个数组交替当源和目标:

第1轮:原数组 → 临时数组
第2轮:临时数组 → 原数组
第3轮:原数组 → 临时数组
...
最后如果数据落在临时数组里,用 参数_数组 = 局变_临时_归并 一次性拷回。

和其他归并排序的区别
普通归并        自然归并        混合归并
初始分段        固定步长1        扫描自然段        扫描自然段
后续轮次        步长翻倍        重新扫描        复用边界
完全升序        O(n log n)        O(n)        O(n)
完全随机        O(n log n)        O(n log n) + 扫描        O(n log n)
额外开销        无        每轮O(n)扫描        只1次扫描 + 边界数组
混合归并 = 自然归并的发现能力 + 普通归并的扫描效率。首趟扫描利用了数据的自然有序性(少段就少合并),后续轮次不再扫描又省掉了自然归并每轮 O(n) 的额外开销。



999999.png 88888888888.png


  
窗口程序集名保 留  保 留备 注
程序集8   
子程序名返回值类型公开备 注
排序模块_混合归并排序4 
参数名类 型参考可空数组备 注
参数_数组整数型
参数_升序逻辑型
变量名类 型静态数组备 注
局变_个数整数型 
局变_分段计数整数型 
局变_游标首整数型0
局变_游标尾_整数型0
局变_开始位置整数型 
局变_临时_归并整数型0
局变_源是临时逻辑型 
n1整数型 
m1整数型 
局变_个数 = 取数组成员数 (参数_数组)
如果真 (局变_个数 ≤ 1)
返回 ()
重定义数组 (局变_临时_归并, 假, 局变_个数)
重定义数组 (局变_游标首, 假, 局变_个数)
重定义数组 (局变_游标尾_, 假, 局变_个数)
如果真 (是否为空 (参数_升序))
参数_升序 = 真
' ── 第1轮:扫描自然段边界 ──
局变_分段计数 = 1
局变_开始位置 = 1
循环判断首 ()
局变_游标首 [局变_分段计数] = 局变_开始位置
局变_游标尾_ [局变_分段计数]子程序_计算尾部 (参数_数组, 局变_游标首 [局变_分段计数])
局变_开始位置 = 局变_游标尾_ [局变_分段计数] + 1
如果 (局变_开始位置 > 局变_个数)
跳出循环 ()
局变_分段计数 = 局变_分段计数 + 1

循环判断尾 ()
' ── 快速判断 ──
如果 (局变_分段计数 = 1)
如果 (参数_升序 = )
返回 ()
数组模块_反转数组_整数型 (参数_数组)
返回 ()

如果 (局变_分段计数 = 局变_个数)
如果 (参数_升序 = )
数组模块_反转数组_整数型 (参数_数组)
返回 ()
返回 ()

' 继续归并

' ── 后续轮:两两归并,复用边界 ──
局变_源是临时 = 假
判断循环首 (局变_分段计数 > 1)
' ── 第1个循环:只做合并 ──
变量循环首 (1, 局变_分段计数, 2, n1)
如果 (局变_源是临时 = )
子程序_合并4 (参数_数组, 局变_临时_归并, 局变_游标首 [n1], 局变_游标尾_ [n1], 局变_游标首 [n1 + 1], 局变_游标尾_ [n1 + 1])
子程序_合并4 (局变_临时_归并, 参数_数组, 局变_游标首 [n1], 局变_游标尾_ [n1], 局变_游标首 [n1 + 1], 局变_游标尾_ [n1 + 1])

变量循环尾 ()
' ── 第2个循环:只更新边界数组 ──
m1 = 1
变量循环首 (1, 局变_分段计数, 2, n1)
局变_游标首 [m1] = 局变_游标首 [n1]
局变_游标尾_ [m1] = 局变_游标尾_ [n1 + 1]
如果真 (局变_游标尾_ [m1] = 0)
局变_游标尾_ [m1] = 局变_游标尾_ [n1]
m1 = m1 + 1
变量循环尾 ()
' ── 清零:防止下一轮读到旧数据 ──
局变_游标首 [m1] = 0
局变_游标尾_ [m1] = 0
局变_分段计数 = m1 - 1
局变_源是临时 = 取反 (局变_源是临时)
判断循环尾 ()
' ── 最终数据在临时数组,拷回原数组 ──
如果真 (局变_源是临时 = )
参数_数组 = 局变_临时_归并

子程序名返回值类型公开备 注
子程序_合并4  
参数名类 型参考可空数组备 注
参数_源整数型
参数_目标整数型
参数_左首整数型
参数_左尾整数型
参数_右首整数型
参数_右尾整数型
变量名类 型静态数组备 注
局变_i整数型 
局变_j整数型 
局变_写整数型 
' ── 落单:右首=0 右尾=0,直接拷贝左段然后返回 ──
如果 (参数_右首 = 0 参数_右尾 = 0)
内存拷贝_数组到数组 (参数_目标 [参数_左首], 参数_源 [参数_左首], (参数_左尾 - 参数_左首 + 1) × 4)
返回 ()


' ── 正常合并两段 ──
局变_i = 参数_左首
局变_j = 参数_右首
局变_写 = 参数_左首
循环判断首 ()
如果 (局变_i > 参数_左尾)
跳出循环 ()
如果 (局变_j > 参数_右尾)
跳出循环 ()



如果 (参数_源 [局变_i] ≤ 参数_源 [局变_j])
参数_目标 [局变_写] = 参数_源 [局变_i]
局变_i = 局变_i + 1
参数_目标 [局变_写] = 参数_源 [局变_j]
局变_j = 局变_j + 1
局变_写 = 局变_写 + 1
循环判断尾 ()
' ── 左段剩余 ──
判断循环首 (局变_i ≤ 参数_左尾)
参数_目标 [局变_写] = 参数_源 [局变_i]
局变_i = 局变_i + 1
局变_写 = 局变_写 + 1
判断循环尾 ()
' ── 右段剩余 ──
判断循环首 (局变_j ≤ 参数_右尾)
参数_目标 [局变_写] = 参数_源 [局变_j]
局变_j = 局变_j + 1
局变_写 = 局变_写 + 1
判断循环尾 ()
子程序名返回值类型公开备 注
子程序_计算尾部整数型 
参数名类 型参考可空数组备 注
参数_数组整数型
参数_游标首整数型
变量名类 型静态数组备 注
n1整数型 
如果 (参数_游标首 + 1 > 取数组成员数 (参数_数组))
返回 (参数_游标首)


变量循环首 (参数_游标首, 取数组成员数 (参数_数组) - 1, 1, n1)
如果 (参数_数组 [n1] ≤ 参数_数组 [n1 + 1])

返回 (n1)

变量循环尾 ()
返回 (取数组成员数 (参数_数组))


  
DLL命令名返回值类型公开备 注
内存拷贝_数组到数组 
DLL库文件名:
kernel32.dll
在DLL库中对应命令名:
RtlMoveMemory
参数名类 型传址数组备 注
目标首元素整数型, 传目标数组[1]等,自动得到jz
源首元素整数型, 传源数组[1]等,自动得到jz
长度整数型, 拷贝的字节数


排序.zip

199.24 KB, 下载次数: 15, 下载积分: 精币 -2 枚

评分

参与人数 1精币 +1 收起 理由
kyo9766 + 1 感谢分享,很给力!~

查看全部评分


签到天数: 23 天

发表于 2026-7-9 01:00:15 | 显示全部楼层   山东省青岛市
支持开源~!感谢分享你的内容
回复 支持 反对

使用道具 举报

结帖率:75% (3/4)

签到天数: 12 天

 楼主| 发表于 2026-7-9 00:05:03 | 显示全部楼层   福建省福州市
本帖最后由 APPLEUFO 于 2026-7-9 00:10 编辑
  
子程序名返回值类型公开备 注
子程序_合并_自然混合3 两路归并(短数组驱动版)
参数名类 型参考可空数组备 注
参数_原始段落整数型
参数_目标段落整数型
参数_左首整数型
参数_左尾整数型
参数_右首整数型
参数_右尾整数型
变量名类 型静态数组备 注
n1整数型 
m1整数型 
x1整数型 
' ── 孤儿段检测 ──
如果真 (参数_右首 = 0)
内存拷贝_数组到数组 (参数_目标段落 [参数_左首], 参数_原始段落 [参数_左首], (参数_左尾 - 参数_左首 + 1) × 4)
返回 ()
' ── 保存写入起点(合并区域起始位置)──
x1 = 参数_左首
' ── 天生把短的放左边,只写一套代码 ──
' 两段都是有序的,谁前谁后对合并结果无影响
如果真 (参数_左尾 - 参数_左首 > 参数_右尾 - 参数_右首)
交换变量 (参数_左首, 参数_右首)
交换变量 (参数_左尾, 参数_右尾)

' ── 短段(左)驱动,长段(右)配对 ──
' 变量循环首会重置 n1,没法用 n1=n1-1 抵消,改用循环判断首手动管 n1
n1 = 参数_左首
m1 = 参数_右首
循环判断首 ()
如果 (参数_原始段落 [n1] ≤ 参数_原始段落 [m1])
' 短 ≤ 长 → 写短,推进 n1
参数_目标段落 [x1] = 参数_原始段落 [n1]
n1 = n1 + 1
x1 = x1 + 1
如果真 (n1 > 参数_左尾)
' 短段耗尽,拷贝长段剩余
内存拷贝_数组到数组 (参数_目标段落 [x1], 参数_原始段落 [m1], (参数_右尾 - m1 + 1) × 4)
返回 ()

' 长 < 短 → 写长,推进 m1
参数_目标段落 [x1] = 参数_原始段落 [m1]
m1 = m1 + 1
x1 = x1 + 1
如果真 (m1 > 参数_右尾)
' 长段耗尽,拷贝短段剩余
内存拷贝_数组到数组 (参数_目标段落 [x1], 参数_原始段落 [n1], (参数_左尾 - n1 + 1) × 4)
返回 ()


循环判断尾 ()

V3 优化思路通俗解释
先说原版(V1)在干嘛
两段有序数组合并,标准做法就是两个游标各指着一段,比一比谁小,小的写出去、游标前进,循环往复。

左段:[1, 4, 7]    右段:[2, 3, 9]

① 1 vs 2 → 写1,左游标前进
② 4 vs 2 → 写2,右游标前进
③ 4 vs 3 → 写3,右游标前进
④ 4 vs 9 → 写4,左游标前进
⑤ 7 vs 9 → 写7,左游标前进
⑥ 左段没了 → 拷贝右段剩余 [9]
原版每写一个元素,要检查两次:左段空了吗?右段空了吗?

但问题在于——你这一轮只推进了一边,另一边根本没动,查它纯属浪费。

V3 的第一个优化:只查被推进的那一边
你在 if 分支(左≤右)里推进了左游标,那就只在 if 分支里查左段耗尽。

你在 else 分支(右<左)里推进了右游标,那就只在 else 分支里查右段耗尽。

耗尽检查从每轮 2 次砍到 1 次。

V3 的第二个优化:交换左右,只写一套代码
第一点有个前提——你得知道哪边是短段,哪边是长段。短段一定先走完,长段可能先走完也可能后走完。

但左右段是对称的,如果左短走一套代码,右短又得写一套镜像代码,太啰嗦。

关键思路:合并前先比一下两段长度,如果左段更长就交换左右的首尾指针。

交换前:左段[5,6,7,8,9]  右段[1,2,3]     → 左长右短
交换后:左段[1,2,3]      右段[5,6,7,8,9]  → 左短右长
两段都是有序的,谁在前谁在后对合并结果没有任何影响——反正都是从头比到尾,小的先写。

交换后短段永远在左边,后面就只需要写一套"左短右长"的逻辑,不用镜像翻转。

V3 完整代码
子程序_合并_自然混合3

1. 孤儿段检测:右段不存在(右首=0),直接拷贝左段,返回
2. 保存写入起点 x1 = 左首(交换前保存,交换后左首就变了)
3. 如果左段比右段长,交换左右首尾 → 短的永远在左
4. n1=左首(短段游标),m1=右首(长段游标)
5. 循环判断首:
     如果 左[n1] ≤ 右[m1]:
         写左[n1],n1前进,x1前进
         如果 n1 > 左尾:短段没了,拷贝右段剩余,返回
     否则:
         写右[m1],m1前进,x1前进
         如果 m1 > 右尾:长段没了,拷贝左段剩余,返回
   循环判断尾
为什么快了 10%
V1 原版        V3 优化版
每轮耗尽检查        2 次(查两边,一半是白查)        1 次(只查推进的那边)
代码路径        不区分短长        交换后短必在左,一套代码
百万级实测        2.235s        2.016s
省掉的那一半检查,在百万级数据下就是每个元素少一次比较+一次跳转判断,累积起来约 10% 的提升。

一句话总结
原版每轮查两边谁空了,但每次只动了一边,另一边的检查纯属浪费。V3 把耗尽检查塞进各自分支里——动了谁就查谁。再加上交换左右使短段永远在左,一套代码搞定,不用镜像翻转。百万级数据稳定快约 10%。

继续改进 又加速了  归并排序10%的速度     把归并排序普通的双指针推进,改成了单指针推进  替换 上一楼的 子程序_合并_自然混合   就可用了


回复 支持 反对

使用道具 举报

结帖率:75% (3/4)

签到天数: 12 天

 楼主| 发表于 2026-7-8 00:27:38 | 显示全部楼层   福建省福州市
  
子程序名返回值类型公开备 注
排序模块_归并排序_自然混合_整数型 
参数名类 型参考可空数组备 注
参数_数组整数型
参数_升序逻辑型
变量名类 型静态数组备 注
局变_数组成员个数整数型 
局变_分段计数整数型 
局变_游标首整数型0
局变_游标尾_整数型0
局变_临时_归并整数型0
局变_源是临时逻辑型 
n1整数型 
aaa类模块_计时器_性能调试 
局变_数组成员个数 = 取数组成员数 (参数_数组)  ' ── 边界检查:0或1个元素不用排 ──
如果真 (局变_数组成员个数 ≤ 1)
返回 ()

如果真 (是否为空 (参数_升序))  ' ── 参数_升序 默认为真(省略时按升序排)──
参数_升序 = 真

局变_分段计数 = 子程序_计算游标边界 (参数_数组, 局变_游标首, 局变_游标尾_)
如果 (局变_分段计数 = 1)  ' 快速判断:利用扫描结果直接处理两种极端情况   ' 整个数组只有一个自然段 → 已经升序排好了' 升序:直接返回;降序:反转即可
如果 (参数_升序 = )
返回 ()
数组模块_反转数组_整数型 (参数_数组)
返回 ()

如果 (局变_分段计数 = 局变_数组成员个数)  ' 每个元素自成一档 → 完全降序' 升序:反转;降序:已经排好直接返回
如果 (参数_升序 = )
数组模块_反转数组_整数型 (参数_数组)
返回 ()
返回 ()



' 既不是完全升序也不是完全降序,继续归并


重定义数组 (局变_临时_归并, 假, 局变_数组成员个数)
局变_源是临时 = 假
判断循环首 (局变_分段计数 > 1)  ' 后续轮:两两归并,复用边界' 每轮段数大约减半,直到只剩1个段(排序完成)' 用两个数组交替当源和目标(双缓冲),避免每轮全数组拷贝
变量循环首 (1, 局变_分段计数, 2, n1)  ' ── 第1个循环:只做合并 ── ' 步长2,每次取相邻两段合并
如果 (局变_源是临时 = )
子程序_合并_自然混合 (参数_数组, 局变_临时_归并, 局变_游标首 [n1], 局变_游标尾_ [n1], 局变_游标首 [n1 + 1], 局变_游标尾_ [n1 + 1])
子程序_合并_自然混合 (局变_临时_归并, 参数_数组, 局变_游标首 [n1], 局变_游标尾_ [n1], 局变_游标首 [n1 + 1], 局变_游标尾_ [n1 + 1])

变量循环尾 ()
' ── 第2个循环:只更新边界数组 ──  ' 两段合并后新段的首=左段首,新段的尾=右段尾 ' 落单段(右段尾读出0)的尾=自己的尾  ' 同时清零边界外一格,防止下一轮读到旧数据
局变_分段计数 = 子程序_游标边界收束 (局变_游标首, 局变_游标尾_, 局变_分段计数)
局变_源是临时 = 取反 (局变_源是临时)  ' ── 切换源/目标(双缓冲)── ' 第1轮:原数组→临时数组,第2轮:临时数组→原数组,交替写
判断循环尾 ()
' 最终回写   ' 如果排序结果落在临时数组(经过奇数轮归并),拷回原数组   ' EPL数组赋值 参数_数组=局变_临时_归等价于整块内存拷贝
如果真 (局变_源是临时 = )
交换变量 (参数_数组, 局变_临时_归并)
' 参数_数组 = 局变_临时_归并

子程序名返回值类型公开备 注
子程序_计算游标边界整数型 返回 分段计数   千问
参数名类 型参考可空数组备 注
参数_数组整数型
参数_游标首整数型
参数_游标尾_整数型
变量名类 型静态数组备 注
n1整数型 
局变_数组长度整数型 
局变_当前段数整数型 
局变_数组长度 = 取数组成员数 (参数_数组)
重定义数组 (参数_游标首, 假, 局变_数组长度)
重定义数组 (参数_游标尾_, 假, 局变_数组长度)
局变_当前段数 = 1
参数_游标首 [1] = 1
' ── 单层循环扫描 ──
计次循环首 (局变_数组长度 - 1, n1)
' 遇到降序断点,当前段结束,新段开始
如果真 (参数_数组 [n1] > 参数_数组 [n1 + 1])
参数_游标尾_ [局变_当前段数] = n1
局变_当前段数 = 局变_当前段数 + 1
参数_游标首 [局变_当前段数] = n1 + 1

计次循环尾 ()
' 最后一个段的尾部永远是数组末尾
参数_游标尾_ [局变_当前段数] = 局变_数组长度
返回 (局变_当前段数)
子程序名返回值类型公开备 注
子程序_游标边界收束整数型 返回 新的分段计数
参数名类 型参考可空数组备 注
参数_游标首整数型
参数_游标尾_整数型
参数_旧分段计数整数型
变量名类 型静态数组备 注
m1整数型 
n1整数型 
变量循环首 (1, 参数_旧分段计数, 2, n1)
m1 = m1 + 1
参数_游标首 [m1] = 参数_游标首 [n1]  ' 新段的首 = 左段的首
参数_游标尾_ [m1] = 参数_游标尾_ [n1 + 1]  ' 新段的尾 = 右段的尾(两段合并后,新段尾就是右段尾)
变量循环尾 ()
如果真 (参数_游标尾_ [m1] = 0)
参数_游标尾_ [m1]取数组成员数 (参数_游标首)

' ── 清零边界外一格 ──' 下一轮合并时,最后一个n1+1会读到这一格   ' 如果不清零,残留的旧值会被当成右段首/尾,导致合并越界
连续赋值 (0, 参数_游标首 [m1 + 1], 参数_游标尾_ [m1 + 1])
返回 (m1)
子程序名返回值类型公开备 注
子程序_合并_自然混合 两路归并(恢复简洁版)
参数名类 型参考可空数组备 注
参数_源整数型
参数_目标整数型
参数_左首整数型
参数_左尾整数型
参数_右首整数型
参数_右尾整数型
变量名类 型静态数组备 注
局变_i整数型 
局变_j整数型 
局变_写整数型 
' ── 孤儿段检测 ──
如果真 (参数_右首 = 0)
内存拷贝_数组到数组 (参数_目标 [参数_左首], 参数_源 [参数_左首], (参数_左尾 - 参数_左首 + 1) × 4)
返回 ()

局变_i = 参数_左首
局变_j = 参数_右首
局变_写 = 参数_左首
循环判断首 ()
如果 (参数_源 [局变_i] ≤ 参数_源 [局变_j])
参数_目标 [局变_写] = 参数_源 [局变_i]
局变_i = 局变_i + 1
参数_目标 [局变_写] = 参数_源 [局变_j]
局变_j = 局变_j + 1

局变_写 = 局变_写 + 1
如果真 (局变_i > 参数_左尾)
内存拷贝_数组到数组 (参数_目标 [局变_写], 参数_源 [局变_j], (参数_右尾 - 局变_j + 1) × 4)
返回 ()

如果真 (局变_j > 参数_右尾)
内存拷贝_数组到数组 (参数_目标 [局变_写], 参数_源 [局变_i], (参数_左尾 - 局变_i + 1) × 4)
返回 ()

循环判断尾 ()


改了最后赋值 换成 交换变量
回复 支持 反对

使用道具 举报

签到天数: 23 天

发表于 2026-6-17 09:38:39 | 显示全部楼层   浙江省宁波市
感谢分享,支持开源!!!
回复 支持 反对

使用道具 举报

结帖率:75% (3/4)

签到天数: 12 天

 楼主| 发表于 2026-6-16 13:40:58 | 显示全部楼层   福建省福州市
  
子程序名返回值类型公开备 注
子程序_游标边界收束1整数型 返回 新的分段计数
参数名类 型参考可空数组备 注
参数_游标首整数型
参数_游标尾_整数型
参数_旧分段计数整数型
变量名类 型静态数组备 注
m1整数型 
n1整数型 
变量循环首 (1, 参数_旧分段计数, 2, n1)
m1 = m1 + 1
参数_游标首 [m1] = 参数_游标首 [n1]  ' 新段的首 = 左段的首
参数_游标尾_ [m1] = 参数_游标尾_ [n1 + 1]  ' 新段的尾 = 右段的尾(两段合并后,新段尾就是右段尾)
变量循环尾 ()
如果真 (参数_游标尾_ [m1] = 0)
参数_游标尾_ [m1]取数组成员数 (参数_游标首)

' ── 清零边界外一格 ──' 下一轮合并时,最后一个n1+1会读到这一格   ' 如果不清零,残留的旧值会被当成右段首/尾,导致合并越界
连续赋值 (0, 参数_游标首 [m1 + 1], 参数_游标尾_ [m1 + 1])
返回 (m1)


子程序_游标边界收束   更新了    一个循环解决   中间 不用 判断  ,结尾再修复异常


回复 支持 反对

使用道具 举报

结帖率:50% (1/2)

签到天数: 17 天

发表于 2026-6-15 17:40:05 | 显示全部楼层   广东省汕头市
感谢大神分享~!
回复 支持 反对

使用道具 举报

签到天数: 14 天

发表于 2026-6-15 09:21:58 | 显示全部楼层   湖北省武汉市
感谢分享 +1
回复 支持 反对

使用道具 举报

结帖率:100% (4/4)

签到天数: 22 天

发表于 2026-6-15 08:04:08 | 显示全部楼层   山东省淄博市
感谢分享
回复 支持 反对

使用道具 举报

结帖率:96% (503/522)

签到天数: 20 天

发表于 2026-6-14 22:49:43 | 显示全部楼层   内蒙古自治区乌海市
感谢分享
回复 支持 反对

使用道具 举报

结帖率:75% (3/4)

签到天数: 12 天

 楼主| 发表于 2026-6-14 15:14:43 | 显示全部楼层   福建省福州市
  
窗口程序集名保 留  保 留备 注
程序集8   
子程序名返回值类型公开备 注
排序模块_混合归并排序4 
参数名类 型参考可空数组备 注
参数_数组整数型
参数_升序逻辑型
变量名类 型静态数组备 注
局变_数组成员个数整数型 
局变_分段计数整数型 
局变_游标首整数型0
局变_游标尾_整数型0
局变_临时_归并整数型0
局变_源是临时逻辑型 
n1整数型 
' ── 边界检查:0或1个元素不用排 ──
局变_数组成员个数 = 取数组成员数 (参数_数组)
如果真 (局变_数组成员个数 ≤ 1)
返回 ()

' ── 参数_升序 默认为真(省略时按升序排)──
如果真 (是否为空 (参数_升序))
参数_升序 = 真

' ══════════════════════════════════════
' 第1轮:扫描自然段边界
' 从左到右找"降序断点"(a[i]>a[i+1]),把每个自然升序段的首尾下标记下来
' 比如数组 [3,7,12, 5,9, 2,6,8,15] 扫描完得到3个段:
' 段1: 首=1 尾=3  (3,7,12)
' 段2: 首=4 尾=5  (5,9)
' 段3: 首=6 尾=9  (2,6,8,15)
' ══════════════════════════════════════
' 计时器启动 ()
局变_分段计数 = 子程序_计算游标边界4 (参数_数组, 局变_游标首, 局变_游标尾_)
' 调试输出 (“计算边界4”)
' 计时器结束 ()
' 子程序1_调试输出所有游标边界 (局变_游标首, 局变_游标尾_)
' ══════════════════════════════════════
' 快速判断:利用扫描结果直接处理两种极端情况
' ══════════════════════════════════════
如果 (局变_分段计数 = 1)
' 整个数组只有一个自然段 → 已经升序排好了
' 升序:直接返回;降序:反转即可
如果 (参数_升序 = )
返回 ()
数组模块_反转数组_整数型 (参数_数组)
返回 ()

如果 (局变_分段计数 = 局变_数组成员个数)
' 每个元素自成一档 → 完全降序
' 升序:反转;降序:已经排好直接返回
如果 (参数_升序 = )
数组模块_反转数组_整数型 (参数_数组)
返回 ()
返回 ()

' 既不是完全升序也不是完全降序,继续归并


' ══════════════════════════════════════
' 后续轮:两两归并,复用边界
' 每轮段数大约减半,直到只剩1个段(排序完成)
' 用两个数组交替当源和目标(双缓冲),避免每轮全数组拷贝
' ══════════════════════════════════════
重定义数组 (局变_临时_归并, 假, 局变_数组成员个数)
局变_源是临时 = 假
判断循环首 (局变_分段计数 > 1)
' ── 第1个循环:只做合并 ──
' 步长2,每次取相邻两段合并
' 比如5个段:合并(1,2)、合并(3,4)、段5落单
' 游标首[n1+1]超出已填充范围时自然为0,子程序_合并4检测到右首=0就走孤儿路径
变量循环首 (1, 局变_分段计数, 2, n1)
如果 (局变_源是临时 = )
子程序_合并4 (参数_数组, 局变_临时_归并, 局变_游标首 [n1], 局变_游标尾_ [n1], 局变_游标首 [n1 + 1], 局变_游标尾_ [n1 + 1])
子程序_合并4 (局变_临时_归并, 参数_数组, 局变_游标首 [n1], 局变_游标尾_ [n1], 局变_游标首 [n1 + 1], 局变_游标尾_ [n1 + 1])

变量循环尾 ()
' ── 第2个循环:只更新边界数组 ──
' 两段合并后新段的首=左段首,新段的尾=右段尾
' 落单段(右段尾读出0)的尾=自己的尾
' 同时清零边界外一格,防止下一轮读到旧数据
局变_分段计数 = 子程序_游标边界收束 (局变_游标首, 局变_游标尾_, 局变_分段计数)
' ── 切换源/目标(双缓冲)──
' 第1轮:原数组→临时数组,第2轮:临时数组→原数组,交替写
局变_源是临时 = 取反 (局变_源是临时)
判断循环尾 ()
' ══════════════════════════════════════
' 最终回写
' 如果排序结果落在临时数组(经过奇数轮归并),拷回原数组
' EPL数组赋值 参数_数组=局变_临时_归等价于整块内存拷贝
' ══════════════════════════════════════
如果真 (局变_源是临时 = )
参数_数组 = 局变_临时_归并

' ══════════════════════════════════════════════════════════════════════
' 子程序_游标边界收束
' 合并完一轮后,把旧边界压缩成新边界
' 比如旧5个段合并后变3个段:
' 旧: [1,3] [4,5] [6,9] [10,12] [13,15]
' 新: [1,5] [6,12] [13,15]    ← 第3个是落单段,尾=自己的尾
' 返回新的分段计数
' ══════════════════════════════════════════════════════════════════════
子程序名返回值类型公开备 注
子程序_游标边界收束整数型 返回 新的分段计数
参数名类 型参考可空数组备 注
参数_游标首整数型
参数_游标尾_整数型
参数_旧分段计数整数型
变量名类 型静态数组备 注
m1整数型 
n1整数型 
m1 = 1
变量循环首 (1, 参数_旧分段计数, 2, n1)
' 新段的首 = 左段的首
参数_游标首 [m1] = 参数_游标首 [n1]
' 新段的尾 = 右段的尾(两段合并后,新段尾就是右段尾)
参数_游标尾_ [m1] = 参数_游标尾_ [n1 + 1]
' 如果读出0,说明n1是最后一段且没有右段(落单段)
' 落单段没有合并,尾就是自己的尾
如果 (参数_游标尾_ [m1] = 0)  ' 奇数段落单
参数_游标尾_ [m1] = 参数_游标尾_ [n1]
' 偶数正好不用管

m1 = m1 + 1
变量循环尾 ()
' ── 清零边界外一格 ──
' 下一轮合并时,最后一个n1+1会读到这一格
' 如果不清零,残留的旧值会被当成右段首/尾,导致合并越界
参数_游标首 [m1] = 0
参数_游标尾_ [m1] = 0
m1 = m1 - 1
返回 (m1)
' ══════════════════════════════════════════════════════════════════════
' 子程序_计算游标边界
' 扫描数组,找出所有自然升序段的首尾下标
' 自然升序段:从某个位置开始,a[i]≤a[i+1] 连续成立的最长子序列
' 遇到 a[i]>a[i+1] 就是一个"降序断点",当前段到此结束
' ══════════════════════════════════════════════════════════════════════
子程序名返回值类型公开备 注
子程序_计算游标边界4整数型 返回 分段计数   千问
参数名类 型参考可空数组备 注
参数_数组整数型
参数_游标首整数型
参数_游标尾_整数型
变量名类 型静态数组备 注
i整数型 
数组长度整数型 
当前段数整数型 
数组长度 = 取数组成员数 (参数_数组)
重定义数组 (参数_游标首, 假, 数组长度)
重定义数组 (参数_游标尾_, 假, 数组长度)
当前段数 = 1
参数_游标首 [1] = 1
' ── 单层循环扫描 ──
计次循环首 (数组长度 - 1, i)
' 遇到降序断点,当前段结束,新段开始
如果真 (参数_数组 [i] > 参数_数组 [i + 1])
参数_游标尾_ [当前段数] = i
当前段数 = 当前段数 + 1
参数_游标首 [当前段数] = i + 1

计次循环尾 ()
' 最后一个段的尾部永远是数组末尾
参数_游标尾_ [当前段数] = 数组长度
返回 (当前段数)
子程序名返回值类型公开备 注
子程序1_调试输出所有游标边界  
参数名类 型参考可空数组备 注
参数_游标首整数型
参数_游标尾整数型
变量名类 型静态数组备 注
n1整数型 
计次循环首 (取数组成员数 (参数_游标首), n1)
调试输出 (n1, , 参数_游标首 [n1], 参数_游标尾 [n1])
计次循环尾 ()
' ══════════════════════════════════════════════════════════════════════
' 子程序_合并4
' 两路归并:从源数组取两段,合并写入目标数组
' 参数_右首=0 参数_右尾=0 时,说明只有左段没有右段(落单段)
' 直接把左段整块拷贝到目标数组,不做合并
' 否则:标准两路归并,小的先写入,用完一段后剩余段批量拷贝
' ══════════════════════════════════════════════════════════════════════
子程序名返回值类型公开备 注
子程序_合并4 两路归并(改进版:先比大小,后判断退出)
参数名类 型参考可空数组备 注
参数_源整数型
参数_目标整数型
参数_左首整数型
参数_左尾整数型
参数_右首整数型
参数_右尾整数型
变量名类 型静态数组备 注
局变_i整数型 
局变_j整数型 
局变_写整数型 
' 孤儿段检测:右段首=0 表示没有合并对象
如果 (参数_右首 = 0)
内存拷贝_数组到数组 (参数_目标 [参数_左首], 参数_源 [参数_左首], (参数_左尾 - 参数_左首 + 1) × 4)
返回 ()


局变_i = 参数_左首
局变_j = 参数_右首
局变_写 = 参数_左首
循环判断首 ()
如果 (参数_源 [局变_i] ≤ 参数_源 [局变_j])
参数_目标 [局变_写] = 参数_源 [局变_i]
局变_i = 局变_i + 1
参数_目标 [局变_写] = 参数_源 [局变_j]
局变_j = 局变_j + 1
局变_写 = 局变_写 + 1
如果真 (局变_i > 参数_左尾)
内存拷贝_数组到数组 (参数_目标 [局变_写], 参数_源 [局变_j], (参数_右尾 - 局变_j + 1) × 4)
返回 ()
如果真 (局变_j > 参数_右尾)
内存拷贝_数组到数组 (参数_目标 [局变_写], 参数_源 [局变_i], (参数_左尾 - 局变_i + 1) × 4)
返回 ()

循环判断尾 ()
子程序名返回值类型公开备 注
  
' ══════════════════════════════════════════════════════════════════════
' 排序模块_混合归并排序4
' 算法:首趟扫描自然段边界(O(n)),后续轮复用边界做归并不重新扫描
' 优势:升序O(n)直接返回,随机数据比普通归并快,比自然归并省掉每轮扫描
' ══════════════════════════════════════════════════════════════════════



i支持库列表   支持库注释   
spec特殊功能支持库
   压缩包 测试

完全 改 排序.zip

201.09 KB, 下载次数: 2, 下载积分: 精币 -2 枚

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备2025452707号) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表