快慢指针之原地处理数组/链表
leetcode26.删除排序数组中的重复项
由于数组已经排序,所以重复的元素一定连在一起,找出它们并不难,但如果毎找到一个重复元素就立即删除它,就是在数组中间进行删除操作,整个时间复杂度是会达到 O(N^2)。
我们让慢指针slow
走在后面,快指针fast
走在前面探路,找到一个不重复的元素就告诉slow
并让slow
前进一步。这样当fast
指针遍历完整个数组nums
后,nums[0..slow]
就是不重复元素。
int removeDuplicates(int[] nums) {
if (nums.length == 0) {
return 0;
}
int slow = 0, fast = 0;
while (fast < nums.length) {
if (nums[fast] != nums[slow]) {
slow++;
// 维护 nums[0..slow] 无重复
nums[slow] = nums[fast];
}
fast++;
}
// 数组长度为索引 + 1
return slow + 1;
}
再简单扩展一下,如果给你一个有序链表,如何去重呢?这是力扣第 83 题,其实和数组去重是一模一样的,唯一的区别是把数组赋值操作变成操作指针而已:
ListNode deleteDuplicates(ListNode head) {
if (head == null) return null;
ListNode slow = head, fast = head;
while (fast != null) {
if (fast.val != slow.val) {
// nums[slow] = nums[fast];
slow.next = fast;
// slow++;
slow = slow.next;
}
// fast++
fast = fast.next;
}
// 断开与后面重复元素的连接
slow.next = null;
return head;
}
leetcode27.移除元素
如果fast
遇到需要去除的元素,则直接跳过,否则就告诉slow
指针,并让slow
前进一步。
int removeElement(int[] nums, int val) {
int fast = 0, slow = 0;
while (fast < nums.length) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
fast++;
}
return slow;
}
注意这里和有序数组去重的解法有一个重要不同,我们这里是先给nums[slow]
赋值然后再给slow++
,这样可以保证nums[0..slow-1]
是不包含值为val
的元素的,最后的结果数组长度就是slow
。
leetcode283.
题目让我们将所有 0 移到最后,其实就相当于移除nums
中的所有 0,然后再把后面的元素都赋值为 0 即可。
void moveZeroes(int[] nums) {
// 去除 nums 中的所有 0
// 返回去除 0 之后的数组长度
int p = removeElement(nums, 0);
// 将 p 之后的所有元素赋值为 0
for (; p < nums.length; p++) {
nums[p] = 0;
}
}
// 见上文代码实现
int removeElement(int[] nums, int val);