在leetcode题库上的移除元素问题(C语言)
今天刚写了一道leetcode上的题,觉得这题的解题思维真的是太棒了。
希望能分享给大家
先看题:
1.给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组,限时间复杂度为O(n)。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
请在下面函数作答:
先说一下,这些刷题网大致是分两种的,一种是直接给你一个函数接口,你只需要完成函数接口部分即可;(今天这题是这种)
另一种是什么都没有,你必须自己写出完整的c代码,也就是从main函数开始写,头文件都要自己引用;
首先,我们来分析一下题目的意思:
- nums中可能有任意多的数,且不都相同,你需要在nums中找到一个或多个val的值,然后原地删除;
- 不能用额外的数组,如果可以使用的话,就有一种比较简单的方法;
- 空间复杂度为O(1),时间复杂度O(n),就意味着你不能使用像顺序表删除元素一样,找到val值,就开始删除,删除的过程就是将val值用后一个数往前覆盖,如此下来时间复杂度就会有O(n2);
- 原地删除举个例子:在[ 1,2,1,5,4] 中删掉所有的1,你可以删成这样[ 2,5,4,0,0] 或者[ 2,5,4,1,1]我们不需要关系4后面的数是多少,只要数组前面是2,4,5的排列组合就算完成任务;
先介绍一下用额外数组的方法,因为我们可以从这个方法过渡到一种非常神奇的思想:
图一:自己创建一个数组,并个给定要删除的值,并给出删除方法
图二:判断元素,不是删除的数,放入新建数组中,依次判断
图三:当判断到有删除数时,不放入新建数组
图四:判断完后,得到的新建数组
总结:这个方法虽然时间复杂度是满足要求O(n),但是空间复杂度不满足,实际上空间复杂度也是O(n);
接下来就是非常神奇的思想的讲解,如何即不创建数组又把时间复杂度控制在O(n)内:
假设我们开始有两个不同颜色的标记,nums数组跟val值都跟上面假设一致
图五:准备两个指针均指向首地址
接下来就是这个思想的过程:
图六:前三个都是数据不变,到了第四步,指向了被删除的数,黄色不动,黑走
图七:看图就明白了 图八:看图就明白了
至此,我们就完成了不用额外数组也可以完成删除我们所要选的数据,且都满足了空间复杂度为O(1),时间复杂度O(n);
刚开始、现在删除完、之前新建数组删完的对比:
图九:对比结果
那问题又来了,那该如何解决访问到倒数第二个5就停止访问呢?
其实很简单,记录下每次黑色遇到val值 2就可以了
那就我们直接上代码吧
1 int removeElement(int* nums, int numsSize, int val) 2 { 3 int* p1 = nums;//黑色标记 4 int* p2 = nums;//黄色标记 5 int count = 0;//记录下每次黑色遇到val值 6 while (p1 < nums + numsSize)//黑色指到最后一个元素的下一个元素就停下 7 { 8 if (*(p1) != val)//不是val,存入黄色标记处 9 { 10 *(p2) = *(p1); 11 p2++;//黄色标记向后加一 12 } 13 else//是val记录 14 { 15 count++; 16 } 17 p1++;//黑色标记向后加一 18 } 19 return numsSize - count;//返回总大小 - val出现的次数 20 } 21 #include <stdio.h> 22 int main()//测试 23 { 24 int arr[] = { 3,1,6,3,5,3,7 }; 25 int sz = sizeof(arr) / sizeof(arr[0]); 26 int ret = removeElement(arr, sz, 3); 27 printf("%d ", ret); 28 return 0; 29 }
运行结果:
跟我们的预期结果一致;
(注:只要前四个是1 , 6 , 5 , 7 的排列组合就可以了)
总结:这个方法本质来讲就是用双指针来解决问题,如果想了解更多可以到leetcode的官网去看看更多大神的操作;
更新
2022-03-05
12:05:01