前端算法技巧锻炼之双指针法

1,870 阅读3分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

本文题目均来自LeetCode,每道题都会有多种解法,因为我们主要是为了练习双指针,所以只考虑用双指针解题

什么是双指针

  • 双指针:指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。
  • 双指针法充分使用了数组有序这一特征,从而在某些情况下能够简化一些运算。
  • 换言之,只要我们看到有序数组,那么就可以考虑使用双指针解法。

题目一

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

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

测试用例

  • [1,1,2]

解析

  • 读题,划重点。也就是找出题目中的关键词。本题中我们可以看到,源数组是有序的,最终的结果是要返回一个没有重复元素的数组的长度,同时解题要求是,必须在原地(即必须在原数组上操作),不能利用另外的数组。
  • 理解题目要求之后,开始思考解题思路:
  • 首先,数组存在为空的情况,此时没有任何重复元素,满足题目条件,那么我们直接返回0即可。
  • 在数组不为空的情况下:(我们利用快慢指针)
    • 创建一个变量i指向数组的第一位;
    • 然后从数组第二位开始迭代数组,在此过程中,如果 nums[i] 不等于 nums[j]时,我们先将 i++,接着将nums[i]的值替换为nums[j],以此类推
    • 最后返回 i+1

看代码:

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

迭代步骤

  • 源数组 [1, 1, 2, 3, 4] 初始化的 i = 0; j = 1
  • 第一次迭代:nums[0] === nums[1],不进入我们的处理逻辑;
  • 第二次迭代:i = 0; j = 2; nums[i]为1,nums[j]为2,进入处理逻辑,将 i 赋值为 1,nums[1] 赋值为 2;
  • 第三次迭代:i = 1; j = 3; nums[i]为2,nums[j]为3,进入处理逻辑,将 i 赋值为 2,nums[2] 赋值为 3;
  • 第四次迭代:i = 2; j = 4; nums[i]为3,nums[j]为4,进入处理逻辑,将 i 赋值为 3,nums[3] 赋值为 4;
  • 第五次迭代,j = 5,不满足循环的前置条件,所以循环结束。此时数组为 [1, 2, 3, 4]i的值为3
  • 因为我们一开始没有去更改nums[0]的值,所以返回的是 i + 1

练习题

  • 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

要求:不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。