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

Scala中如何修改传入方法的数组引用?附LeetCode移除元素场景

解决Scala中LeetCode移除元素的原地修改问题

问题场景(LeetCode移除元素问题)

给定数组nums和目标值target,需要原地移除所有等于target的元素并返回新长度,要求使用O(1)额外空间,不能分配新数组。举个实际例子:

输入:nums = [0,1,2,2,3,0,4,2],target=2
要求修改后数组为[0,1,3,0,4],返回长度5

实现思路

  • 识别所有等于target的元素,将其移至数组右侧并统计数量;
  • 保留数组左侧的有效元素(非target值);
  • 返回原数组长度减去目标元素的统计数,即为有效元素的新长度。

现有代码与问题分析

先看你编写的Scala代码:

object RemoveElement { // Link to question: https://leetcode.com/problems/remove-element/
  def main(args: Array[String]): Unit = {
    var nums = Array(3,2,2,3)
    val target = 3
    val result = removeElement(nums, target)
    // nums = nums.dropRight(_.equals(target)) // POINT 1
    println(s"Result: ${result}, Modified Array: [${nums.mkString(", ")}]")
  }

  def removeElement(nums: Array[Int], target: Int): Int = {
    val n = nums.length
    var left, counter = 0
    var right = n - 1
    while(left < right){
      if(nums(left) != target){
        left += 1
      } else {
        // Find position of the elements which is not equal to target
        if(nums(right) == target){
          counter += 1
          right -= 1
        } else{
          // Swap the elements
          counter += 1
          val temp = nums(left)
          nums(left) = nums(right)
          nums(right) = temp
          left += 1
          right -= 1
        }
      }
    }
    // nums.dropWhile(_.equals(target)) // POINT 2
    // nums = nums.dropRight(_.equals(target)) // POINT 3
    return (n - counter)
  }
}

针对代码里的几个关键点,我来拆解分析:

  • POINT 1:main方法里修改nums是可行的,因为这里的nums是var类型,允许重新赋值。但dropRight的用法有误——它需要接收一个整数表示移除的数量,而非谓词,正确写法应该是nums = nums.dropRight(counter)。
  • POINT 2:这条语句对原数组无任何影响,因为dropWhile会返回一个新数组,而你没有将返回值赋值给任何变量,原数组nums(方法参数里的val)不会被修改。
  • POINT 3:这里报错是因为removeElement方法里的nums是val类型——Scala方法参数默认是val,虽然数组本身是可变的(可修改内部元素),但你不能给这个val参数重新赋值。

核心疑问解答:Scala中如何原地修改传入的集合并让调用方感知?

要解决这个问题,得先理清Scala集合的可变/不可变特性,以及方法参数的规则:

1. 数组的特殊性:可变容器,但参数默认是val

数组(Array[T])本身是可变的——哪怕方法参数是val类型,你依然可以修改数组任意索引位置的元素值。但你不能给这个val参数重新赋值(比如nums = 新数组),这是语法层面的限制。

2. 满足LeetCode要求的正确做法

LeetCode的“原地修改”核心要求是:确保数组前N个元素为有效元素(N是返回的长度),不需要真的“删除”后面的元素(判题时只会检查前N个元素是否正确)。如果想让调用方看到的数组只包含有效元素,有两种方式:

方式一:调用方截断数组

你的现有代码已经把所有非target元素移到了数组左侧,只是后面还残留target元素。此时在main方法里,用返回的长度截断数组即可:

// main方法里修改这一行:
nums = nums.take(result)

take(result)会返回原数组前result个元素组成的新数组,赋值给var类型的nums后,输出的就是正确的有效数组。

方式二:仅调整元素位置(满足LeetCode判题)

其实LeetCode并不要求你真的删除数组尾部元素,只要前result个元素是正确的非target值即可。你的交换逻辑已经实现了这一点——比如测试用例nums = [3,2,2,3],target=3,运行后数组会变成[2,2,3,3],返回的result是2,LeetCode判题时只会检查前2个元素[2,2],完全符合要求。

3. 通用场景:修改传入的集合

  • 对于可变集合(比如scala.collection.mutable.ArrayBuffer),即使方法参数是val,你也可以直接调用它的可变方法(如remove、add)修改集合内容,调用方会感知到变化,因为可变集合是引用类型,方法操作的是同一个实例。
  • 对于不可变集合(比如List、Vector),你无法修改原集合,只能返回一个新集合,让调用方重新赋值。

修正后的完整代码

这里给出修正后的代码,既满足LeetCode判题要求,又能在main方法中输出正确的数组:

object RemoveElement {
  def main(args: Array[String]): Unit = {
    var nums = Array(3,2,2,3)
    val target = 3
    val result = removeElement(nums, target)
    nums = nums.take(result) // 截断数组到有效长度
    println(s"Result: ${result}, Modified Array: [${nums.mkString(", ")}]")
  }

  def removeElement(nums: Array[Int], target: Int): Int = {
    val n = nums.length
    var left = 0
    var counter = 0
    var right = n - 1
    while(left < right){
      if(nums(left) != target){
        left += 1
      } else {
        if(nums(right) == target){
          counter += 1
          right -= 1
        } else{
          // 交换元素,把非target元素移到左边
          val temp = nums(left)
          nums(left) = nums(right)
          nums(right) = temp
          counter += 1
          left += 1
          right -= 1
        }
      }
    }
    // 最后检查中间的元素(当left == right时)
    if(left == right && nums(left) == target){
      counter +=1
    }
    n - counter
  }
}

注意我补充了最后一步检查:当left和right相遇时,如果该位置元素是target,需要计入计数器,避免漏算这种边界情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:05:44