🍼题目
🥛思路
数组的理论知识:数组的元素在 内存 地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
使用JS的内置方法splice删除元素,但是如果一道题目可以内置的方法解决,那这种情况我们就不要用,很明显不是考我们使用内置方法的使用技巧。
如果一道题目使用内置方法仅仅是解决问题的一部分,并且我们知道内置方法里面实现的内部过程,时间复杂度,我们就可以使用。
let nums = [1, 2, 3, 4, 5];
let val = 3;
// 查找要删除的元素的索引
let index = nums.indexOf(val);
// 如果找到了元素,则使用 splice 删除它
if (index !== -1) {
nums.splice(index, 1); // 从 index 位置开始删除 1 个元素
}
console.log(nums.length);
这里介绍三种解决方法:
- 暴力解法
- 双指针
- 双指针优化
暴力解决法时间复杂度:O(n^2) 空间复杂度:O(1)
双指针:
- 时间复杂度:O(n),其中 n 为序列的长度。我们只需要遍历该序列至多两次。
- 空间复杂度:O(1)。我们只需要常数的空间保存若干变量。
🍹暴力解法
两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
function removeElement(nums, val) {
let size = nums.length;
for (let i = 0; i < size; i++) {
if (nums[i] === val) {
for (let j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
}
i--;
size--;
}
}
return size;
}
🍺双指针
双指针法(也被称为快慢指针法)是一种优化算法的技巧,它通过使用两个指针(一个快指针和一个慢指针)在单个for循环中完成原本可能需要多个循环或更复杂逻辑才能完成的任务。这种方法特别适用于在数组或链表中需要同时追踪多个位置或进行元素比较的场景。
定义快慢指针
- 快指针:遍历整个数组或链表,寻找那些满足特定条件(例如,不等于目标值)的元素
- 慢指针:指向一个新数组的当前位置,用于记录下一个满足条件的元素应该放置的位置。
function removeElement(nums, val) {
// 定义慢指针和块指针,指向数组头部
let slowIndex = 0;
for (let fastIndex = 0; fastIndex < nums.length; fastIndex++) {
if (nums[fastIndex] !== val) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
🥂双指针优化
题目上写着:元素的顺序可能发生改变
如果需要移除的元素刚好就位于数组起始位置,比如[1,2,3,4,5]中的1,传统做法需整体左移元素。但根据题目元素的顺序可能发生改变的条件,我们可直接将末尾元素5移至首位替代1,得[5,2,3,4],此优化在val元素较少时显著提升效率。
还是使用双指针的方式,不过这次使用的是双指针从数组的两端向中间逼近。
实现:如果左指针left所指元素等于val,则将右指针right所指元素复制到left处,随后right左移。若right复制的元素等于val,则继续此过程,即用right指向的新元素覆盖left处(原left处等于val的值被覆盖),直至left处元素不等于val。
function removeElement(nums, val) {
let left = 0;
let right = nums.length;
while (left < right) {
if (nums[left] === val) {
nums[left] = nums[right - 1];
right--;
} else {
left++;
}
}
return left;
}
const nums = [3, 2, 2, 3];
console.log(removeElement(nums, 3));
🍵总结
双指针相对暴力解决法来说,时间复杂度更低,实际应用中,通常会选择时间复杂度更低的方法(如方法二或方法三)以提高性能。
📖参考笔记:代码随想录