本文记录一下力扣网上涉及数组的问题:移除元素。
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。
假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:
更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
返回 k。
示例 1:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,_,_,_]
解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。
注意这五个元素可以任意顺序返回。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
题目中提到 "原地"操作 指的是在不额外申请内存空间(或仅使用常数级额外空间)的前提下,直接修改原始数据结构的内容。
也就是说不能额外使用空间,在原有数组进行移动;可以使用双指针进行处理,有两种思路:
第一种思路是双指针从前往后遍历;
第二种思路是双指针,一个指向数组首部,一个指针指向数组尾部;
1. 定义两个指针 left 和 right,使用 left指针指向新数组的当前位置;
2. 使用 right指针遍历原数组,进行判断;
3. 当 nums[right] != val时,将 nums[right] 赋值给 nums[left];
循环退出后,新数组长度即为left。
C语言实现如下:
//双指针/从前往后
int removeElement(int* nums, int numsSize, int val) {
int left = 0;
int right = 0;
while(right < numsSize) {
if(nums[right] != val){
nums[left] = nums[right];
left++;
}
right++;
}
return left;
}
可以看到,这里没有申请额外的空间,在原来数组上进行的移动。
1. 定义左右指针Left和 right:
left指针:始终指向新数组的下一个可写入位置。
right指针:指向数组末尾,用于提供可替换的有效元素(即 != val的元素);
2. 遍历数组,遍历当前元素:
当 nums[left] == val时:
寻找数组末尾可以替换的元素;检查右指针所指元素是否等于val;
若 nums[right] != val:将其值赋给 nums[left],并将 left 右移一位(因为该位置已被有效元素填充)。
无论是否找到有效元素,都将 right 左移一位(缩小有效范围)。
当前元素不等于 val 时(nums[left] != val):
直接将 left 右移一位,继续检查下一个元素。
3. 当left > right时,left就是最新的数组长度;
当 left > right 时循环终止。此时有两种可能:
right 指针向左越过了 left 指针(即 right = left - 1)
left 指针向右越过了 right 指针(即 left = right + 1)
无论哪种情况,最终状态都是 left 指向第一个未被处理的位置,而 right 指向最后一个被处理的有效元素的前一个位置。
C语言实现如下:
//双指针/从数组首尾开始处理
int removeElement(int* nums, int numsSize, int val) {
int left = 0;
int right = numsSize-1;
while(left <= right) {
if(nums[left] == val) {
if(nums[right] != val) {
nums[left] = nums[right];
left++;
}
right--;
}
else { //nums[left] != val
left++;
}
}
return left;
}