You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无显式返回值时归并排序为何修改列表?含递归栈及Java相关困惑

搞懂Python中mergeSort无返回值却修改原列表的问题

嘿,我完全理解你的困惑——刚接触递归和Python对象模型的时候,这种“没return却改了原列表”的情况确实让人摸不着头脑。咱们一步步拆解清楚:

一、为什么没显式return,原列表还是被修改了?

核心原因在于Python对可变对象的参数传递机制:

  • 列表是可变对象,当你把alist传给mergeSort函数时,函数拿到的是原列表的引用(不是整个列表的副本)。
  • 在merge阶段(就是代码里把左右子列表合并的部分),我们直接通过alist[k] = ...这样的语句修改了原列表的元素值——因为引用指向的是同一个内存里的列表,所以这些修改会直接反映到函数外部的原列表上。

对比一下:如果传的是整数、字符串这种不可变对象,函数里修改的只是它们的副本,原对象不会变;但列表、字典这种可变对象,修改操作会直接作用在原对象上。

二、递归调用在栈中是怎么返回的?

递归的本质是利用调用栈来管理每一层的函数执行,咱们以你的mergeSort为例:

  1. 拆分阶段(压栈):每次调用mergeSort,如果列表长度大于1,就把它拆成左右两半,然后分别递归调用mergeSort(left_half)和mergeSort(right_half)。每一次递归调用都会被压入调用栈,直到拆分到单个元素(这就是递归的base case,因为单个元素本身就是有序的)。
  2. 合并阶段(回溯出栈):当某个递归调用遇到base case(列表长度≤1),就会执行完当前函数的剩余代码(也就是merge部分),然后从栈中弹出,回到上一层递归。上一层递归拿到已经排好序的左右子列表,执行merge操作把它们合并成有序的子列表,再弹出栈回到更上层……直到最顶层的mergeSort(alist)执行完merge,整个原列表就排好序了。

举个小例子:比如列表[3,1,2],调用栈的过程大概是:

  • 压入mergeSort([3,1,2]),拆分出[3]和[1,2]
  • 压入mergeSort([3]),触发base case,执行完弹出栈
  • 压入mergeSort([1,2]),拆分出[1]和[2]
  • 压入mergeSort([1]),触发base case,弹出栈
  • 压入mergeSort([2]),触发base case,弹出栈
  • 回到mergeSort([1,2]),合并[1]和[2]得到[1,2],弹出栈
  • 回到mergeSort([3,1,2]),合并[3]和[1,2]得到[1,2,3],弹出栈,整个过程结束

三、你需要补充哪些信息才能彻底理解?

  • Python可变/不可变对象的区别:搞清楚哪些对象是可变的(列表、字典、集合),哪些是不可变的(int、str、tuple),以及它们在函数传参时的行为差异。
  • 递归的栈帧模型:理解调用栈如何保存每一层递归的上下文(比如局部变量、返回地址),base case的作用,以及回溯过程是怎么回事。
  • mergeSort的具体实现细节:重点看merge阶段是如何把排序后的子列表元素赋值回原列表的——这是原列表被修改的直接原因。

完整的mergeSort代码示例

def mergeSort(alist):
    print("Splitting ", alist)
    if len(alist) > 1:
        mid = len(alist) // 2
        left_half = alist[:mid]  # 创建左半部分的副本
        right_half = alist[mid:]  # 创建右半部分的副本

        mergeSort(left_half)  # 递归排序左半副本
        mergeSort(right_half)  # 递归排序右半副本

        # 合并左右排序后的副本到原列表
        i = j = k = 0
        while i < len(left_half) and j < len(right_half):
            if left_half[i] < right_half[j]:
                alist[k] = left_half[i]
                i += 1
            else:
                alist[k] = right_half[j]
                j += 1
            k += 1

        # 处理左半剩余元素
        while i < len(left_half):
            alist[k] = left_half[i]
            i += 1
            k += 1

        # 处理右半剩余元素
        while j < len(right_half):
            alist[k] = right_half[j]
            j += 1
            k += 1
    print("Merging ", alist)

# 测试
alist = [54,26,93,17,77,31,44,55,20]
mergeSort(alist)
print(alist)  # 输出: [17, 20, 26, 31, 44, 54, 55, 77, 93]

你看,虽然递归时排序的是left_half和right_half这两个副本,但最后我们把它们的元素逐个赋值回原alist的对应位置,所以原列表就被修改成有序的了。

内容的提问来源于stack exchange,提问作者heretoinfinity

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:57:41