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

LeetCode买卖股票问题:Array与List递归性能差异原因探究

关于LeetCode「买卖股票的最佳时机」递归实现的性能差异问题

我在解决LeetCode的「买卖股票的最佳时机」问题时,采用了两种递归实现方案,性能差异显著,具体如下:

方案1:将Array转换为List后递归处理

def maxProfit(prices: Array[Int]): Int = {
  @tailrec
  def maxProfitHelp(prices: List[Int], maxProfit: Int, lastBuy: Int): Int = {
    prices match {
      case Nil => maxProfit
      case head :: tail => if (head > lastBuy)  {
        maxProfitHelp(tail, Math.max(maxProfit, head - lastBuy), lastBuy)
      } else {
        maxProfitHelp(tail, maxProfit, head)
      }
    }
  }

  val listPrices = prices.toList
  maxProfitHelp(listPrices.tail, 0, listPrices.head)
}

该方案耗时962ms,内存占用71.70MB。

方案2:直接对原始Array进行递归处理

def maxProfitWithoutList(prices: Array[Int]): Int = {
  @tailrec
  def maxProfitHelp(prices: Array[Int], maxProfit: Int, lastBuy: Int): Int = {
    prices match {
      case c if c.isEmpty => maxProfit
      case c => if (c.head > lastBuy)  {
        maxProfitHelp(c.tail, Math.max(maxProfit, c.head - lastBuy), lastBuy)
      } else {
        maxProfitHelp(c.tail, maxProfit, c.head)
      }
    }
  }

  maxProfitHelp(prices.tail, 0, prices.head)
}

该方案耗时9134ms,内存占用75.47MB。

可见Array递归的性能比List递归差10倍!请问造成这种差异的原因是什么?


问题解答

核心原因在于Scala中List和Array的底层结构以及tail操作的实现逻辑完全不同:

  • List的特性:
    Scala的List是不可变单向链表,每个节点仅保存当前元素和下一个节点的引用。调用tail时只是返回下一个节点的引用,无元素复制或内存分配,操作复杂度为O(1);同时head :: tail的模式匹配是编译器优化后的高效操作,几乎无额外开销。尾递归过程中每一步都只是传递引用,整体时间复杂度为O(n)。

  • Array的特性:
    Scala的Array本质是JVM原生连续内存数组。调用tail方法时会创建一个新数组,复制原数组从索引1开始的所有元素,操作复杂度为O(n)。递归时每次调用tail都会复制剩余元素,总复制次数为n+(n-1)+...+1 = n(n+1)/2,实际时间复杂度为O(n²);加上Array的模式匹配无法像List那样优化,每次匹配和tail操作都涉及新对象创建,进一步放大了性能和内存开销。

即便两个方法都用@tailrec实现了尾递归优化,Array版本的tail操作带来的O(n)复制开销无法消除,这就是两者性能差距悬殊的根本原因。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 15:42:39