如何修改归并排序实现仅对正数排序且保持负数原位置不变?
问题背景
需求为修改归并排序算法,实现仅对数组内正数元素排序、所有负数元素保留在原始位置不移动的效果。
原有标准归并排序会全量排序所有元素,直接在合并逻辑中添加“仅元素大于0时执行替换”的判断会出现异常:要么负数发生位移,要么正数排序错乱。测试输入为[57, 16, -70, -66, -59, -18, 43, -26, -99, 41],错误修改后的输出为[57, 43, -70, 41, 43, 57, 16, -70, 16, 57],期望输出为[16, 41, -70, -66, -59, -18, 43, -26, -99, 57]。
原有归并排序实现代码:
def merge_sort(arr): if len(arr) > 1: mid = len(arr)//2 left = arr[:mid] right = arr[mid:] merge_sort(left) merge_sort(right) i = j = k = 0 while i < len(left) and j < len(right): if left[i] < right[j]: arr[k] = left[i] i+=1 else: arr[k] = right[j] j+=1 k+=1 while i < len(left): arr[k] = left[i] i+=1 k+=1 while j < len(right): arr[k] = right[j] j+=1 k+=1
错误原因
直接在合并判断中添加正数校验的方案存在本质逻辑缺陷:
- 归并拆分阶段的左右子数组全量包含正、负数,没有区分需要固定位置的负数和需要排序的正数
- 合并阶段的k指针按数组全长度逐位移动,遇到负数时如果跳过赋值,会导致后续元素覆盖错位、指针不同步,最终出现元素重复、顺序错乱的问题
正确实现方案
不需要改动原有归并排序的核心逻辑,只需要增加一层位置处理逻辑,全程不触碰负数的存储位置:
- 遍历原数组,记录所有正数的索引位置和对应值
- 用原有归并排序对提取出的纯正数列表做排序
- 将排好序的正数按顺序填回之前记录的原正数索引位置,负数全程不做任何修改
完整可运行代码:
def merge_sort(arr): if len(arr) > 1: mid = len(arr)//2 left = arr[:mid] right = arr[mid:] merge_sort(left) merge_sort(right) i = j = k = 0 while i < len(left) and j < len(right): if left[i] < right[j]: arr[k] = left[i] i+=1 else: arr[k] = right[j] j+=1 k+=1 while i < len(left): arr[k] = left[i] i+=1 k+=1 while j < len(right): arr[k] = right[j] j+=1 k+=1 return arr def sort_positive_keep_negative(arr): # 提取正数的位置和值 pos_indexes = [] pos_values = [] for idx, num in enumerate(arr): if num > 0: pos_indexes.append(idx) pos_values.append(num) # 归并排序正数列表 sorted_pos = merge_sort(pos_values) # 回填正数到原位置 for idx, val in zip(pos_indexes, sorted_pos): arr[idx] = val return arr # 测试验证 if __name__ == "__main__": test_arr = [57, 16, -70, -66, -59, -18, 43, -26, -99, 41] print(sort_positive_keep_negative(test_arr)) # 输出:[16, 41, -70, -66, -59, -18, 43, -26, -99, 57],完全匹配期望结果
如果强行在归并递归拆分、合并流程中直接实现该效果,需要在每一层拆分、合并时全程跟踪负数位置,合并时遇到负数位置直接保留原值、移动指针不做比较赋值,逻辑复杂度高且容易出边界问题,远不如上述方案稳定易维护。
内容的提问来源于stack exchange,提问作者Turchick
相关产品推荐
相关产品推荐

