代码随想录 第一章 数组 27.移除元素
代码随想录 第一章 数组 27.移除元素
题目:
-
题目链接:27.移除元素
-
题目描述:
给你一个数组
nums
和一个值val
,你需要 原地 移除所有数值等于val
的元素。元素的顺序可能发生改变。然后返回nums
中与val
不同的元素的数量。假设
nums
中不等于val
的元素数量为k
,要通过此题,您需要执行以下操作:- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。 - 返回
k
。
- 更改
一、思想
快慢指针算法的核心思想是使用两个指针以不同的速度遍历数据结构(如链表或数组):
- 双指针:使用两个指针,一个快指针,一个慢指针。
- 速度差异:快指针移动速度快(如每次两步),慢指针速度慢(如每次一步)。
- 相遇条件:通过速度差,快指针最终会追上慢指针或达到特定条件,用于检测循环或找到特定位置。
二、代码
class Solution
{
public:
/**
* @brief 移除数组中所有指定值的元素(双指针原地修改)
* @param nums 输入输出参数-待处理数组,修改后前n个元素为有效元素
* @param val 需要移除的目标值
* @return int 移除后新数组的长度
*
* @算法思路 快慢指针法:
* 1. 快指针fastIndex扫描全部元素
* 2. 慢指针slowIndex记录非val元素的位置
* 3. 当遇到非val元素时,将其复制到slowIndex位置并右移
*
* @时间复杂度 O(n) 只需一次遍历
* @空间复杂度 O(1) 原地修改,无额外空间
*/
int removeElement(vector<int> &nums, int val)
{
int slowIndex = 0; // 新数组的写入指针(最终位置即为新长度)
// 快指针遍历整个数组,时间复杂度O(n)
for (int fastIndex = 0; fastIndex < nums.size(); ++fastIndex)
{
// 过滤目标值:当元素值不等于val时,执行复制操作
if (nums[fastIndex] != val)
{
// 将有效元素复制到新位置,并移动慢指针
nums[slowIndex++] = nums[fastIndex];
}
// 当元素等于val时,快指针继续前进,慢指针保持不动
}
return slowIndex; // 返回有效数组长度
}
};
三、解析
1 算法工作原理分解
1.1 快慢指针的分工
-
快指针 (
fastIndex
) :- 负责遍历整个数组,检查每个元素是否等于目标值
val
。 - 每次迭代都会向后移动一步。
- 负责遍历整个数组,检查每个元素是否等于目标值
-
慢指针 (
slowIndex
) :- 负责记录下一个非
val
元素应该放置的位置。 - 只有当快指针找到一个非
val
的元素时,慢指针才会移动。
- 负责记录下一个非
1.2 核心逻辑
-
过滤非
val
元素:- 当快指针指向的元素
nums[fastIndex]
不等于val
时,将该元素复制到慢指针的位置nums[slowIndex]
,然后慢指针向后移动一步。
- 当快指针指向的元素
-
跳过
val
元素:- 当快指针指向的元素
nums[fastIndex]
等于val
时,快指针继续向后移动,慢指针保持不动。
- 当快指针指向的元素
1.3 终止条件
- 当快指针遍历完整个数组后,算法结束。
- 此时,慢指针的值
slowIndex
就是数组中非val
元素的个数。
2 关键点说明
2.1 快慢指针的移动规则
-
快指针:
- 每次迭代都会向后移动一步,无论当前元素是否等于
val
。
- 每次迭代都会向后移动一步,无论当前元素是否等于
-
慢指针:
- 只有当快指针找到一个非
val
的元素时,才会向后移动一步。
- 只有当快指针找到一个非
2.2 数组的最终状态
- 数组的前
slowIndex
个元素都是非val
的元素。 - 数组的剩余部分(从
slowIndex
到末尾)可能包含任意值,但这些值不会被使用。
四、复杂度分析
- 时间复杂度:O(n)
快指针需要遍历整个数组一次,最坏情况下需要检查数组中的每个元素(n 为数组长度)。 - 空间复杂度:O(1)
仅需常数级别的额外空间存储指针变量(slowIndex
和fastIndex
)。