适用场景:在原数组上原地删除所有等于目标值的元素,不额外开辟新数组,时间复杂度 O (n),空间复杂度 O (1)。
核心原理:快慢双指针
-
快指针 i :负责从头到尾遍历数组的每一个元素,逐个检查。
for循环每次i++,一定会向后移动。 -
慢指针 j :用来标记存放保留元素的位置 。只有当遍历到的元素不需要删除时,
j才向后移动。数组: [10] [20] [40] [30] [50]
i: →i →i →i →i →i
j: →j →j →j j →j
目标:删除数组中所有值等于30的元素。
右侧最终保留的有效数据: 10 20 40 50
逐步骤推演
原始数组:buf = {10, 20, 40, 30, 50},初始状态 i=0,j=0
i=0,buf[i]=10,不等于 30。执行buf[j++] = buf[i],buf[0]=10,j=1i=1,buf[i]=20,不等于 30。执行buf[j++] = buf[i],buf[1]=20,j=2i=2,buf[i]=40,不等于 30。执行buf[j++] = buf[i],buf[2]=40,j=3i=3,buf[i]=30,等于待删除元素。不执行赋值,j 保持为 3,原地不动i=4,buf[i]=50,不等于 30。执行buf[j++] = buf[i],buf[3]=50,j=4
循环结束。 j=4,代表删除完成之后,数组有效长度为 4。数组前j个元素就是结果:{10,20,40,50}。
重点:数组后面残留的旧数据没有被擦除,我们后续只读取数组前
j项即可。
#include <stdio.h>
#define MAX 5
int main(void)
{
int buf[MAX] = {10,20,40,30,50};
int i,j = 0;
//快慢指针遍历数组
for(i = 0; i < MAX; i++)
{
//当前元素不是要删除的值,则保留
if(buf[i] != 30)
{
buf[j++] = buf[i];
}
}
//打印删除后的有效数组,j为新数组长度
printf("删除30之后的数组:");
for(i = 0; i < j; i++)
{
printf("%d ", buf[i]);
}
printf("\n");
return 0;
}
原理示意图
