力扣算法分析 27.移除元素

1. 题目描述

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

假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:

  • 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
  • 返回 k

示例 1:

复制代码
输入:nums = [3, 2, 2, 3], val = 3
输出:2, nums = [2, 2, _, _]
解释:函数应该返回新的长度 2,并且数组的前两个元素均为 2。

示例 2:

复制代码
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0, 1, 3, 0, 4, _, _, _]
解释:函数应该返回新的长度 5,并且数组的前五个元素均为 0、1、3、0、4。

2. 题解

这道题目要求我们原地移除数组中所有等于 val 的元素。我们可以通过双指针的方法来优化解决。

算法思想:

我们使用双指针来解决这个问题。主要思路如下:

  • left 指针:指向当前数组中有效元素的末尾。

  • right 指针:用于遍历数组,检查每个元素是否需要保留。

具体的操作步骤是:

  1. 初始化

    • left = 0,指向数组的第一个位置,表示当前有效元素的最后一个位置。

    • right = 0,用于遍历整个数组。

  2. 遍历数组

    • 每次循环中,如果 nums[right] != val,则表示该元素不等于 val,我们应该保留它。此时将 nums[left] = nums[right],并且将 left 向右移动,表示下一个有效元素的位置。

    • 如果 nums[right] == val,则跳过该元素,不进行任何操作。

  3. 返回新数组的长度

    • 最终,left 就是数组中不等于 val 的元素个数。

通过双指针的方式,我们只需要遍历数组一次,时间复杂度是 O(n),而空间复杂度是 O(1),符合原地修改数组的要求。

代码实现:

java 复制代码
class Solution {
    public int removeElement(int[] nums, int val) {
        int n = nums.length;  // 获取数组的长度
        int left = 0;  // 初始化左指针,指向数组的起始位置

        // 右指针遍历数组
        for (int right = 0; right < n; right++) {
            // 如果当前元素不等于 val
            if (nums[right] != val) {
                nums[left] = nums[right];  // 将当前元素放到左指针的位置
                left++;  // 左指针右移,指向下一个位置
            }
        }
        return left;  // 返回新数组的长度
    }
}

3. 与本题算法相关的知识点

双指针技术

双指针是解决数组和链表类问题的一种常见技巧。通过两个指针分别从不同方向遍历或在同一方向上操作,可以高效地解决许多问题。在这道题中,双指针的核心思想是:

  • left 指向当前有效数组的最后位置,用来更新不等于 val 的元素。

  • right 用来遍历数组的每个元素,找出不等于 val 的元素,并把它移到 left 指针的位置。

双指针技术的优势是空间复杂度为 O(1),即我们在原地修改数组时,不需要额外的空间。这对于节省内存空间以及提高算法效率是非常有帮助的。

1. 左右指针的基本概念:

在编程中,特别是在数组和链表等数据结构中,"指针" 其实并不是指实际的内存地址,而是用来描述"某个位置"或"指向某个位置"的变量。

在 Java 中,我们没有直接的指针类型(不像 C/C++ 中的指针),但是我们可以使用变量(如 leftright)来模拟指针的行为,表示数组中元素的位置或下标。

2. 为什么 left 代表"当前数组中有效元素的最后位置"?

在这道题目中,不等于 val 的元素需要移到数组的前面,而且我们需要保持这些有效元素的顺序。

  • 我们用 left 来标记新数组(去除 val 后的有效元素)的"末尾"位置。随着遍历数组的进行,left 会不断向右移动,表示我们已经"填充"了数组的某些部分。
3. 左右指针是如何一起工作的:
right 指针:
  • right 用于遍历整个数组。它会依次访问每个元素。

  • 如果当前元素 nums[right] 不等于 val,我们就将这个元素保留下来,放到 left 指针的位置,然后将 left 指针向右移动。

left 指针:
  • left 作为有效元素的"末尾"位置,它开始时指向数组的第一个位置(即下标为 0)。

  • right 找到一个不等于 val 的元素时,left 就会被赋值为该元素,并且 left 之后会向右移动,准备接受下一个不等于 val 的元素。

通过这种方式,left 保持指向"新数组"最后一个有效元素的位置,也就是所有不等于 val 的元素都被移动到数组的前面

4. 具体例子来解释:

假设有如下数组:

java 复制代码
nums = [3, 2, 2, 3, 4]
val = 3

初始化时:

  • left = 0(指向数组的第一个位置)

  • right = 0(指向数组的第一个元素)

然后开始遍历:

  1. 右指针 right = 0 ,当前元素是 3,等于 val,跳过,不做任何操作。

  2. 右指针 right = 1 ,当前元素是 2,不等于 val,将 nums[left] = nums[right],即 nums[0] = 2,然后 left++,使得 left = 1

  3. 右指针 right = 2 ,当前元素是 2,不等于 val,将 nums[left] = nums[right],即 nums[1] = 2,然后 left++,使得 left = 2

  4. 右指针 right = 3 ,当前元素是 3,等于 val,跳过,不做任何操作。

  5. 右指针 right = 4 ,当前元素是 4,不等于 val,将 nums[left] = nums[right],即 nums[2] = 4,然后 left++,使得 left = 3

此时数组变成了:

java 复制代码
nums = [2, 2, 4, 3, 4]

left 最终指向的位置是 3,表示新数组的长度是 3,数组的前 3 个元素 [2, 2, 4] 是有效元素。

5. 为什么移到前面?

这个题目要求的是"原地"修改数组,意思是不能创建新数组,而是要在原数组上操作。因此,我们需要使用 left 指针来控制哪些位置应该填入有效元素。

  • 当我们发现一个不等于 val 的元素时,我们把它移动到数组的前部。left 作为指针,确保我们把这些有效元素填入数组的正确位置。

  • 所有原本在数组后面且等于 val 的元素,将被自然"丢弃"------因为 right 遍历整个数组时,它们不会影响 left 的位置。

原地修改数组

原地修改数组是指在不使用额外数组的情况下,对数组进行修改。在本题中,我们需要确保数组中所有不等于 val 的元素保留在原数组的前面,而不等于 val 的元素被移除。通过双指针的方式,我们将符合条件的元素直接移动到数组的前面,不需要额外开辟新的数组,符合原地修改的要求。

时间复杂度与空间复杂度

  • 时间复杂度:

    遍历数组一次,时间复杂度为 O(n),其中 n 是数组的长度。

  • 空间复杂度:

    我们只使用了常数级别的空间,即 O(1),因为我们没有创建新的数组,而是直接在原数组上进行操作。

4. 总结

这道题目通过使用双指针技术,成功解决了移除元素的问题。我们只需遍历一次数组,时间复杂度为 O(n),空间复杂度为 O(1),是一个高效的解决方案。掌握双指针技术能够帮助我们在处理数组、链表等问题时,避免不必要的空间开销,提高算法效率。

这类题目常见于面试和编程挑战中,熟练掌握双指针技巧对解决许多类似问题至关重要。

相关推荐
VCR__3 小时前
python第三次作业
开发语言·python
码农水水3 小时前
得物Java面试被问:消息队列的死信队列和重试机制
java·开发语言·jvm·数据结构·机器学习·面试·职场和发展
summer_du3 小时前
IDEA插件下载缓慢,如何解决?
java·ide·intellij-idea
wkd_0073 小时前
【Qt | QTableWidget】QTableWidget 类的详细解析与代码实践
开发语言·qt·qtablewidget·qt5.12.12·qt表格
C澒3 小时前
面单打印服务的监控检查事项
前端·后端·安全·运维开发·交通物流
东东5163 小时前
高校智能排课系统 (ssm+vue)
java·开发语言
余瑜鱼鱼鱼3 小时前
HashTable, HashMap, ConcurrentHashMap 之间的区别
java·开发语言
m0_736919103 小时前
模板编译期图算法
开发语言·c++·算法
【心态好不摆烂】3 小时前
C++入门基础:从 “这是啥?” 到 “好像有点懂了”
开发语言·c++
SunnyDays10113 小时前
使用 Java 自动设置 PDF 文档属性
java·pdf文档属性