双指针:快慢指针,滑动窗口

快慢指针

26. 删除有序数组中的重复项

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

思路

使用两个指针进行遍历数组
i 指针指向要返回位置的下一个,另一指针 j 遍历整个原数组
只有当 i 和 j 指向的数不同时,长度加一,即 i++,并将 j 指向的数存入要返回的数组长度中

代码

public int removeDuplicates(int[] nums) {
    int i = 0;
    for(int j = 0; j < nums.length; j++) {
        if(nums[i] != nums[j]) {
            i++;
            nums[i] = nums[j];
        }
    }
    return i + 1;
}

通解解法

如果题目从:每个元素最多出现一次 改为 最多出现k次,那么需要类比一次可以想出以下两点

  • 最多出现k次,那么前k位数应该直接保留
  • 对于后面的数字,它能够保留的前提是:与前面第 k 个数比较,不同则保留

例如:k = 1,假设有:[3,3,3,3,4,4,4,5,5,5]

  1. 首先设定idx = 0,即要插入的位置,目标数组为[]
  2. 先插入前1个数字,即idx = 1,目标数组为[3]
  3. 与前面1个数字比较,直到x = 4时才不同,此时idx++,idx = 2,目标数组为[3,4]
  4. 继续与前1个数字比较,直到x = 5时才不同,此时idx++,idx = 3, 目标数组为[3,4,5]
  5. 继续遍历直到退出循环,返回idx

代码

public int removeDuplicates(int[] nums) {
 	return process(nums, 1);
}
public int process(int[] nums, int k) {
    int idx = 0;
    for(int x : nums) {
        if(idx < k || nums[idx - k] != x) nums[idx++] = x;
    }
    return idx;
}

80. 删除有序数组中的重复项 II

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

思路

有了上面的通解解法,此题直接改变k = 2即可
例如数组:[1,1,1,2,2,3]

  1. 首先设定idx = 0,即要插入的位置,目标数组[]
  2. 加入前2个数字,idx = 2,目标数组[1,1]
  3. 然后与前第2个数字比较,发现1 == nums[idx - 2] ,跳过此数,idx = 2,目标数组[1,1]
  4. 继续与前第2个数字比较,发现2 != nums[idx - 2],此时加入该数,idx = 3,目标数组[1,1,2]
  5. 2 != nums[idx - 2] ,nums[idx] = 2,即将2加入, idx++,idx = 4,目标数组[1,1,2,2]
  6. 3 != nums[idx - 2] ,nums[idx] = 2,即将3加入,idx++,此时原数组遍历完毕,退出循环,idx = 5, 目标数组[1,1,2,2,3]

代码

就将上一题的k = 1改成了k = 2

public int removeDuplicates(int[] nums) {
	return process(nums, 2);
}
public int process(int[] nums, int k) {
    int idx = 0;
    for(int x : nums) {
        if(idx < k || nums[idx - k] != x) nums[idx++] = x;
    }
    return idx;
}

27. 移除元素

https://leetcode-cn.com/problems/remove-element/
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

思路

本题也是双指针的使用,只是 保留条件 发生了改变
依然尝试使用通解解法,只不过 每个元素不允许重复出现 k 次 变为 不允许出现指定元素

代码

public int removeElement(int[] nums, int val) {
    return process(nums, val);//此时的val不代表允许重复的个数了,而是不允许出现的数字
}
public int process(int[] nums, int val) {
    int idx = 0;
    for(int x : nums) {
        if(x != val) nums[idx++] = x;
    }
    return idx;
}

快慢指针总结

  • 双指针中的快慢指针通常是:i 指针保留答案,j 指针遍历数组,一快一慢
  • 通用解法是对 「数据有序」,「相同元素保留k位」 更为本质的解法,主要 考虑在这两点相同的情况下使用
  • 快慢指针中 i 用来指向目标数组, j 用来遍历数组,所以 j 指针可以使用 foreach 循环代替,只需要每次条件成立时改变 i 指针即可
  • 对于「每个元素最多重复k次」和「不允许出现指定元素」只是 元素保留条件发生了改变, 此类题目中,解决过程就是将「保留逻辑」应用到遍历的每一个位置

滑动窗口

思想

滑动窗口:(类似于双指针,不过更像一个窗口的移动)
不断调节子序列的起始位置和终止位置,从而得出我们想要的结论

思路

解决滑动窗口问题主要确定三点

  1. 窗口是什么
  2. 如何移动窗口的右边界
  3. 如何移动窗口的左边界

一般来说,指向窗口的结束位置的指针都是遍历整个数组,而指向窗口的起始位置的指针一般都是根据约束来移动

时间复杂度

一般都是O(n),因为对于每个元素,他只有 进入窗口 和 退出窗口 两次被操作的机会,所以时间复杂度是 2 * n,而不是 n^2

模板

for(枚举选择)
    右边界
    while(不符合条件)
        左边界
    更新结果(根据题意放在while中还是while)

参考

宫水三叶:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shua-chuan-lc-jian-ji-shuang-zhi-zhen-ji-2eg8/
https://leetcode-cn.com/problems/remove-element/solution/shua-chuan-lc-shuang-bai-shuang-zhi-zhen-mzt8/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值