@@ -264,16 +264,84 @@ class Solution:
264
264
265
265
[ 原题链接] ( https://leetcode-cn.com/problems/remove-element/description/ )
266
266
267
- ### 思路
267
+ ### 题意解读
268
268
269
- 双指针 i, j。
269
+ 划一下题目的重点:
270
+
271
+ - 原地移除
272
+ - 不要使用额外的数组空间
273
+ - 不需要考虑数组中超出新长度后面的元素
274
+
275
+ 题目要求我们原地删除所有等于 ` val ` 的元素,不能使用额外空间,且** 不用考虑删除后超出新数组长度后面的元素** 。
276
+
277
+ 也就是说,如果原数组 ` nums ` 长度为 ` x ` ,要删除的 ` val ` 元素个数为 ` y ` ,那么我们只要** 把这 ` n ` 个要删除的元素所在位置用其他有效元素覆盖掉** ,然后返回最终的数组长度 ` x - y ` 。
278
+
279
+ ** 题目并非让我们真的删除数组的元素,而是要改写相关元素的值。**
280
+
281
+ ### 思路阐述
282
+
283
+ 那么要如何进行元素的改写呢?
284
+
285
+ 既然要让 ` val ` 元素都堆在数组尾部,那么我们就派出一个开拓者探路,** 只要遇到非 ` val ` 元素,就把它覆盖到前面来** 。
286
+
287
+ 因此,我们可以定义两个指针:
288
+
289
+ - 快指针 ` j ` :用于寻找非 ` val ` 元素
290
+ - 慢指针 ` i ` :当 ` j ` 找到非 ` val ` 元素时,就被非 ` val ` 元素覆盖
291
+
292
+ ### 图解思路
293
+
294
+ 以题中的 ` nums = [3,2,2,3], val = 3 ` 为例。
295
+
296
+ 开始时 ` i ` 和 ` j ` 都指向下标 0 位置:
297
+
298
+ ![ 初始化时,i = 0, j = 0] ( https://user-gold-cdn.xitu.io/2019/11/21/16e8e78d6bf1eaee?w=425&h=179&f=png&s=8968 )
299
+
300
+ 此时 ` j ` 指向的元素为 ` val ` ,所以把 ` j ` 右移动 1 位:
301
+
302
+ ![ 把快指针 j 右移一位] ( https://user-gold-cdn.xitu.io/2019/11/21/16e8e79c1dd17340?w=425&h=179&f=png&s=8969 )
303
+
304
+ 此时,开拓者 ` j ` 找到了一个非 ` val ` 元素,那么就赋值给 ` i ` 吧:
305
+
306
+ ![ 赋值得到新序列] ( https://user-gold-cdn.xitu.io/2019/11/21/16e8e95a2b497021?w=425&h=251&f=png&s=12570 )
307
+
308
+ 赋值以后,我们得到了一个新的序列 ` [2, 2, 2, 3] ` ,我们可以得知:
309
+
310
+ - ` j ` 指向的元素一定不是 ` val `
311
+ - ` i ` 指向的元素也一定不是 ` val ` ,因为它是从 ` j ` 指向的元素赋值得来的,` j ` 指向非 ` val ` 元素才会进行赋值
312
+
313
+ 这样一来,` i ` 和 ` j ` 都完成了本轮使命,继续前进!
314
+
315
+ 因此每次交换以后,我们都同步增长双指针,令 ` i = i + 1 ` ,` j = j + 1 ` :
316
+
317
+ ![ 同步增长双指针] ( https://user-gold-cdn.xitu.io/2019/11/21/16e8e97cf4ef1990?w=425&h=225&f=png&s=9812 )
318
+
319
+ 此时 ` j ` 又指向了一个非 ` val ` 元素,继续赋值:
320
+
321
+ ![ 再一次赋值得到新序列] ( https://user-gold-cdn.xitu.io/2019/11/21/16e8e989fd262a29?w=425&h=246&f=png&s=12623 )
322
+
323
+ 因为本次 ` i ` 与 ` j ` 指向元素相同,所以赋值后序列没有改变。赋值操作后,我们继续同步增长双指针:
324
+
325
+ ![ 同步增长双指针] ( https://user-gold-cdn.xitu.io/2019/11/21/16e8e9a17c6ecae5?w=425&h=227&f=png&s=9909 )
326
+
327
+ 此时 ` j ` 指向了一个 ` val ` 元素,无法进行赋值操作,继续增长 ` j ` ,令 ` j = j + 1 ` :
328
+
329
+ ![ j 超出数组范围] ( https://user-gold-cdn.xitu.io/2019/11/21/16e8e9b72d7141a7?w=489&h=226&f=png&s=10105 )
330
+
331
+ 此时我们发现 ` j ` 超出数组范围了,循环结束。` [2, 2, 2, 3] ` 即为我们最终所求结果,而红色部分即为新数组长度,长度为 ` len(nums) - (j - i) ` 。
332
+
333
+ ### 总结一下
334
+
335
+ 设置双指针 ` i ` 和 ` j ` ,其中,` j ` 用于寻找非 ` val ` 元素,来覆盖 ` i ` 所指向的元素。
336
+
337
+ - 初始时:设 ` i = 0, j = 0 `
338
+ - 遍历数组:
339
+ - 若 ` nums[j] != val ` :
340
+ - 把 ` j ` 的值赋给 ` i ` :` nums[i] = nums[j] `
341
+ - 同步增长双指针:` i = i + 1, j = j + 1 `
342
+ - 若 ` nums[j] == val ` :
343
+ - ` j ` 变为快指针:` j = j + 1 ` ,寻找下一个非 ` val ` 元素
270
344
271
- - 初始:` i = 0, j = 0 `
272
- - 若 ` nums[j] != val `
273
- - ` nums[i] = nums[j] `
274
- - 同步增长双指针
275
- - 若 ` nums[j] == val `
276
- - j 变为快指针:` j = j + 1 `
277
345
278
346
<!-- tabs: start -->
279
347
@@ -319,9 +387,8 @@ func removeElement(nums []int, val int) int {
319
387
// 去找一个不是 val 的值
320
388
j++
321
389
} else {
322
- // 互换
323
- nums[i], nums[j] = nums[j], nums[i]
324
- // i 在前进的过程中走的是 j 走过的路,一定不会再碰到 val
390
+ // 赋值
391
+ nums[i] = nums[j]
325
392
i++
326
393
j++
327
394
}
@@ -333,6 +400,11 @@ func removeElement(nums []int, val int) int {
333
400
334
401
<!-- tabs: end -->
335
402
403
+ ### 复杂度
404
+
405
+ - 时间复杂度:` O(n) `
406
+ - 空间复杂度:` O(1) ` ,没有使用到额外空间。
407
+
336
408
337
409
## 33. 搜索旋转排序数组
338
410
0 commit comments