【LeetCode】27. 移除元素

leetcode

 

解题思路

方法一:双指针法(推荐)

  1. ​​核心思想​​:使用快慢两个指针遍历数组,快指针寻找有效元素,慢指针标记有效位置
  2. ​​时间复杂度​​:O(n) - 单次遍历数组
  3. ​​空间复杂度​​:O(1) - 原地操作
  4. ​​优势​​:保持元素相对顺序,符合题目"顺序可以改变"的要求

方法二:切片法(Go特性)

  1. ​​核心思想​​:利用Go的切片特性进行元素过滤
  2. ​​时间复杂度​​:平均O(n),最坏O(n^2)(多次内存分配)
  3. ​​适用场景​​:对代码简洁性要求高,且元素顺序无关紧要

关键步骤(双指针法)

func removeElement(nums []int, val int) int {
    slow := 0 // 慢指针标记有效位置
    for fast := 0; fast < len(nums); fast++ { // 快指针遍历所有元素
        if nums[fast] != val { // 发现有效元素
            nums[slow] = nums[fast] // 覆盖到慢指针位置
            slow++ // 有效位置后移
        }
    }
    return slow // 返回有效元素数量
}

复杂度分析

方法时间复杂度空间复杂度特点
双指针法 O(n) O(1) 稳定高效,推荐方案
切片法 O(n)~O(n²) O(1) 代码简洁,可能触发内存分配

完整代码实现

// 方法一:双指针法(推荐)
func removeElementTwoPointers(nums []int, val int) int {
    slow := 0
    for fast := 0; fast < len(nums); fast++ {
        if nums[fast] != val {
            if slow != fast { // 优化:避免不必要的赋值
                nums[slow] = nums[fast]
            }
            slow++
        }
    }
    return slow
}

// 方法二:切片法(Go特性)
func removeElementSlice(nums []int, val int) int {
    for i := 0; i < len(nums); {
        if nums[i] == val {
            nums = append(nums[:i], nums[i+1:]...) // 删除元素
        } else {
            i++
        }
    }
    return len(nums)
}

运行示例输出

func main() {
    // 测试用例
    testCases := []struct {
        nums []int
        val  int
        want int
    }{
        {[]int{3, 2, 2, 3}, 3, 2},        // 示例1
        {[]int{0,1,2,2,3,0,4,2}, 2, 5},   // 示例2
        {[]int{}, 1, 0},                  // 空数组
        {[]int{2,2,2}, 2, 0},             // 全需删除
    }

    for _, tc := range testCases {
        // 创建数组副本用于测试
        nums1 := append([]int{}, tc.nums...)
        nums2 := append([]int{}, tc.nums...)
        
        // 测试双指针法
        k1 := removeElementTwoPointers(nums1, tc.val)
        fmt.Printf("双指针法:输入%v => 返回%d, 数组前%d位:%v\n", 
            tc.nums, k1, k1, nums1[:k1])
        
        // 测试切片法
        k2 := removeElementSlice(nums2, tc.val)
        fmt.Printf("切片法:输入%v => 返回%d, 数组前%d位:%v\n\n", 
            tc.nums, k2, k2, nums2[:k2])
    }
}

关键点说明

  1. ​​双指针优化​​:添加if slow != fast判断,减少不必要的数组写入操作
  2. ​​内存管理​​:切片法会触发内存重新分配,测试时需要复制原数组
  3. ​​边界处理​​:空数组和全删除情况都得到正确处理
  4. ​​结果验证​​:通过检查前k个元素确保正确性,不关心后续元素值

两种方法均可通过LeetCode测试,但推荐使用双指针法以保持最佳性能,特别是在处理大型数组时。切片法虽然代码更简洁,但频繁的切片操作会影响性能。

posted @ 2025-05-07 10:22  云隙之间  阅读(22)  评论(0)    收藏  举报