在日常开发中,我们经常会遇到需要对数组进行去重的场景,特别是处理来自数据库或者外部接口的数据时。例如,一个排序后的用户ID数组中可能存在重复项,而我们需要提取唯一的ID列表。最直观的方法是使用嵌套循环,但这种方法的时间复杂度为 O(n^2),效率较低。今天我们来探讨如何使用双指针法,将数组去重的效率提升到 O(n)。这种优化在处理大数据量的数组时,效果尤为显著。
问题场景重现
假设我们有一个已经排序的整数数组 nums = [1, 1, 2, 2, 3, 4, 4, 5]。我们的目标是原地修改这个数组,使得每个元素只出现一次,并返回新数组的长度。也就是说,修改后的数组应该是 [1, 2, 3, 4, 5],返回长度为 5。注意,我们只能使用 O(1) 的额外空间。
双指针法原理剖析
双指针法的核心思想是使用两个指针,一个慢指针 i 和一个快指针 j。慢指针 i 指向已经处理过的、不重复的元素的最后一个位置。快指针 j 用于遍历整个数组,寻找与 nums[i] 不同的元素。如果 nums[j] 与 nums[i] 不同,则将 nums[j] 复制到 nums[i+1] 的位置,并将 i 向前移动一位。这样,我们就可以在 O(n) 的时间复杂度内完成数组去重。
代码实现(Java)
public class RemoveDuplicates {
public int removeDuplicates(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int i = 0; // 慢指针,指向已处理的不重复元素的最后一个位置
for (int j = 1; j < nums.length; j++) { // 快指针,遍历数组
if (nums[j] != nums[i]) { // 如果快指针指向的元素与慢指针指向的元素不同
i++; // 慢指针向前移动一位
nums[i] = nums[j]; // 将快指针指向的元素复制到慢指针的下一个位置
}
}
return i + 1; // 返回新数组的长度
}
public static void main(String[] args) {
RemoveDuplicates rd = new RemoveDuplicates();
int[] nums = {1, 1, 2, 2, 3, 4, 4, 5};
int len = rd.removeDuplicates(nums);
System.out.println("New length: " + len);
for (int k = 0; k < len; k++) {
System.out.print(nums[k] + " ");
}
System.out.println();
}
}
代码实现(Python)
class Solution:
def removeDuplicates(self, nums: list[int]) -> int:
if not nums:
return 0
i = 0 # 慢指针
for j in range(1, len(nums)): # 快指针
if nums[j] != nums[i]:
i += 1
nums[i] = nums[j]
return i + 1
# 示例
nums = [1, 1, 2, 2, 3, 4, 4, 5]
solution = Solution()
length = solution.removeDuplicates(nums)
print("New length:", length)
print(nums[:length])
实战避坑经验总结
- 空数组处理:在实现算法之前,一定要考虑数组为空的情况,避免空指针异常。Java 代码中,我们首先判断了
nums == null || nums.length == 0,如果为空则直接返回 0。 - 数组越界:在移动慢指针
i之前,一定要确保i的值在有效范围内,否则会发生数组越界异常。 - 原地修改:题目要求原地修改数组,所以我们不能创建新的数组。在双指针移动的过程中,要时刻注意不要覆盖掉有用的数据。
- 理解返回值:该方法返回的是去重后数组的长度,而不是去重后的数组。如果需要获取去重后的数组,需要根据返回的长度截取原始数组。
- 服务器高并发场景:假设这个去重操作是后端 API 的一部分,需要考虑高并发场景下的性能。可以结合 Nginx 做负载均衡,使用 Redis 缓存去重后的结果,避免频繁访问数据库。Nginx 的反向代理功能可以将请求分发到多台服务器上,提高系统的并发连接数和吞吐量。也可以考虑使用像宝塔面板之类的工具来简化服务器的管理。
希望这个例子能帮助你更好地理解双指针法在数组去重中的应用。掌握这种方法,可以有效地提升代码的性能,特别是在处理大数据量的数组时。
冠军资讯
代码旅人