[LeetCode] 27.移除元素

题目:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

题解:

首先,这是一道数组类的简单算法题。我们明确循环不变式(loop invariant)的概念,即一组在循环体内、每次迭代均保持为真的性质。

先贴代码:(GO语言)

func removeElement(nums []int, val int) int {
   left, right, n := 0, 0, len(nums)
   for right < n {
      if nums[right] != val {
         nums[left] = nums[right]
         left++
      }
      right++
   }
   return left
}

我们设置的循环不变式:left指针之前的数组元素(不包含目前所指元素)不为val

  • 初始left设为0,即nums[0]之前的元素不为val,由于nums[0]之前没有元素,left位置满足不变式要求。
  • 接下来的循环中,如果nums[right]不为val,则将nums[right]赋值给nums[left]后后移left,结束本次循环。如果nums[right]val,则直接结束本次循环。每次循环,left位置均满足不变式要求。
  • 最终right指针遍历完数组元素后,结束整个循环,left位置满足不变式要求。

因此在循环结束后,left左侧元素均为非val元素,符合算法要求。

复杂度分析:

  • 时间复杂度:O(n),其中n为序列长度。每个位置至多被遍历两次。
  • 空间复杂度:O(1)。只需要常数的空间存放若干变量。

优化:
我们注意到,题目中还有一个说明,“元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素”,也就是说,我们可以通过保持原数组元素的位置不变来减少赋值次数。
val非常少的时候,原方法会出现大量的冗余赋值,减少赋值次数是对性能的一个大的提升。

先贴代码:(GO语言)

func removeElement(nums []int, val int) int {
   left, right := 0, len(nums)-1
   for right >= left {
      if nums[right] != val {
         nums[left], nums[right] = nums[right], nums[left]
         left++
      } else {
         right--
      }
   }
   return left
}

同样,我们的循环不变量保持不变,即left指针之前的数组元素(不包含目前所指元素)不为val。设置数组首尾指针,循环终止条件为right >= left以保证所有元素均被遍历过一次。循环中维护不变式,最终结果满足题目要求。

复杂度分析:

  • 时间复杂度:O(n),其中n为序列长度。每个位置至多被遍历一次。
  • 空间复杂度:O(1)。只需要常数的空间存放若干变量。

你可能感兴趣的:(leetcode算法)