使用双指针来解决此问题,关键词"有序"数组,一个 index 指针用于构建新数组,一个 i 指针用于遍历整个数组
以下是代码的中文解释以及算法思想:
算法思想
这道题要求对一个有序数组进行去重,使得每个元素最多出现两次。我们需要在原数组上进行操作,不能使用额外的空间。为了实现这个要求,我们可以使用双指针法来完成:
-
初始化两个指针:
index
指针表示下一个可以插入的有效位置(用于构建结果数组),它初始值为 2,因为前两个元素无论如何都可以直接保留。i
指针遍历整个数组,从第三个元素开始检查(因为我们允许每个元素最多出现两次,所以不需要检查前两个元素)。
-
条件检查:
- 对于每个元素
nums[i]
,我们检查它是否等于nums[index - 2]
。如果不等,则表示nums[i]
至少可以插入到当前构建的结果数组中。 - 如果
nums[i] != nums[index - 2]
,则将nums[i]
放到nums[index]
位置上,并将index
指针向后移动一位,准备下一个位置。
- 对于每个元素
-
返回结果:
- 最终
index
的值就是新数组的长度,因为index
指针的值代表了有效数组的长度。 - 原数组前
index
个元素就是去重后的数组,且每个元素最多出现两次。
- 最终
代码示例
java
public class Solution {
public int removeDuplicates(int[] nums) {
if (nums.length <= 2) return nums.length;
int index = 2; // 从第三个元素开始检查
for (int i = 2; i < nums.length; i++) {
// 如果当前元素 nums[i] 不等于 nums[index - 2],说明该元素可以加入
if (nums[i] != nums[index - 2]) {
nums[index] = nums[i];
index++;
}
}
return index;
}
}
具体解释
- 数组长度小于等于 2:如果数组长度不超过 2,直接返回数组长度,因为每个元素都可以出现两次。
- 遍历数组 :
- 从第三个元素开始(
i = 2
),逐个检查每个元素nums[i]
。 - 如果当前元素
nums[i]
不等于nums[index - 2]
,表示当前元素没有超过出现两次的限制。 - 将当前元素
nums[i]
放到nums[index]
位置上,然后将index
向后移动一位。
- 从第三个元素开始(
- 返回结果 :
index
的值就是数组去重后的长度,数组的前index
个元素就是符合要求的结果。
复杂度分析
- 时间复杂度:O(n),因为我们只遍历了一遍数组。
- 空间复杂度:O(1),因为我们在原数组上进行操作,没有使用额外的空间。
示例
假设输入数组为 {1, 1, 1, 2, 2, 3}
:
- 初始状态:
index = 2
- 遍历:
i = 2
,nums[i] = 1
,nums[index - 2] = 1
,相等,跳过此元素。i = 3
,nums[i] = 2
,nums[index - 2] = 1
,不相等,将nums[i]
赋值给nums[index]
,index
增加 1。i = 4
,nums[i] = 2
,nums[index - 2] = 1
,不相等,将nums[i]
赋值给nums[index]
,index
增加 1。i = 5
,nums[i] = 3
,nums[index - 2] = 2
,不相等,将nums[i]
赋值给nums[index]
,index
增加 1。
- 最终结果:返回的长度
index = 5
,即{1, 1, 2, 2, 3}
java 代码如下:
java
class Solution {
public int removeDuplicates(int[] nums) {
// if (nums.length <= 2) return nums.length;
int index = 2;
for(int i = 2; i < nums.length; ++i) {
if(nums[i] != nums[index - 2]) { //注意是index - 2, 而不是 i - 2
nums[index++] = nums[i];
}
}
return index;
}
}
为什么这行代码的判断条件不是nums[i] != nums[i - 2]
java
if (nums[i] != nums[index - 2]) {
这是一个很好的问题。之所以判断条件写成 nums[i] != nums[index - 2]
而不是 nums[i] != nums[i - 2]
,是因为我们希望使用 index
指针来追踪构建新数组的有效长度,并确保每个元素最多出现两次。下面是具体原因:
原因解释
-
index
指针的作用:index
指针代表的是新数组的当前位置,也就是我们构建的、符合条件的数组的长度。index - 2
始终指向新数组的倒数第二个元素。因此,nums[index - 2]
可以用来判断当前的nums[i]
是否已经出现了两次。
-
避免原数组的重复元素影响判断:
- 如果直接用
nums[i - 2]
,它表示的是原数组的倒数第二个元素。然而,我们的目标是构建一个新的符合条件的数组,这个数组并不直接等同于原数组。 - 例如,在原数组中,某个元素可能已经出现多次,但在新数组(由
index
控制)中它可能只出现了一次。因此,判断条件需要使用nums[index - 2]
而不是nums[i - 2]
。
- 如果直接用
-
控制新数组的内容:
- 通过
nums[index - 2]
,我们可以确保只有符合条件的元素才被添加到新数组中,避免出现超过两次的重复。 - 如果使用
nums[i - 2]
,则我们判断的是原数组中的元素而不是新构建的数组,可能会导致多次重复的元素被错误地加入。
- 通过
举例说明
假设输入数组为 {1, 1, 1, 2, 2, 3}
:
-
初始时
index = 2
,我们从第三个元素(i = 2
)开始遍历。- 当
i = 2
时,nums[i] = 1
,如果我们使用nums[i - 2]
进行判断,那就是nums[0] = 1
。此时,它会与nums[i]
相等,判断条件为false
,意味着它会被加入到新数组中,导致1
出现三次,这是不符合题意的。 - 而使用
nums[index - 2]
(即nums[0] = 1
)作为判断条件时,我们可以正确地跳过这个元素,避免多余的重复。
- 当
通过使用 index
指针,我们有效地控制了新数组的内容,并保证每个元素最多出现两次。