双指针算法

一、理论基础

双指针法又称为“快慢指针法”,可以通过一个快指针和一个慢指针在一个 for 循环下完成两个 for 循环的工作。

  • 快指针:寻找新数组元素,新数组就是不含有目标元素的数组
  • 慢指针:指向更新新数组下标的位置

二、 相关题目

1、力扣 15 题:三数之和

 首先需要对数组进行排序,然后有一个 for 循环,确定第一个数i。第二个数和第三个数使用双指针法,第二个数起始位置指向 i+1,第三个数起始位置指向数组的最后一个元素,计算这三个数的和 sum。

  • 第一种情况,sum > 0,说明结果大了,需要把第三个数向前位移一位,即 right--,这样才能让结果小一些,更加接近 0
  • 第二种情况,sum < 0,说明结果小了,需要把第二个数向后位移一位,即 left++,这样才能让结果大一些,更加接近 0
  • 第三种情况,sum = 0,把结果储存到结果集里,并把第二个数向后位移一位,第三个数向前位移一位,即 left++; right--;  为什么要同时操作 left 和 right 呢,因为这三个数的结果已经是 0 了,如果只操作 left ,那么下一轮 while 循环的三数之和必定 > 0,同理,如果只操作 right,那么下一轮 while 循环的三数之和必定 < 0,换言之,这轮循环没有必要进行,我们同时移动这两个指针,把多余的循环跳过即可。

直到遍历完整个数组。题目要求结果集中不能有重复的数组,所以需要对结果集去重。 如何去重呢?如果 nums[i ] 和 nums[i - 1]的值相等,是不是就说明 nums[i] 的情况我们已经算过了,就不需要再算一遍了,此时,直接跳过该次循环。同理,left 和 right 也是如此。

时间复杂度为:O(n²)

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    const res = [];
    // 排序
    nums.sort((a, b) => a - b);
    for (let i = 0; i < nums.length; i++) {
        // 对第一个数去重,即i
        if (i > 0 && nums[i] === nums[i - 1]) {
            continue
        }
        if (nums[i] > 0) break
        let left = i + 1
        let right = nums.length - 1
        while (left < right) {
            // 对第二个数去重,即left
            if (left > i + 1 && nums[left] === nums[left - 1]) {
                left++
                continue
            }
            // 对第三个数去重,即right
            if (right < nums.length - 2 && nums[right] === nums[right + 1]) {
                right--
                continue
            }
            const sum = nums[i] + nums[left] + nums[right]
            if (sum < 0) {
                left++
            } else if (sum > 0) {
                right--
            } else {
                res.push([nums[i], nums[left], nums[right]])
                left++
                right--
            }
        }
    }
    return res;
};

2、力扣 18 题:四数之和

 该题与上一题的思路一致,不过需要先嵌套 for 循环确定第一个数 i 和第二个数 j,之后的思路与上一题一致,重点在于结果集去重。

时间复杂度:O(n³)

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function(nums, target) {
    nums.sort((a, b) => a - b)
    if (nums.length < 4) {
        return []
    }
    const res = []
    for (let i = 0; i < nums.length; i++) {
        // 第一个数去重
        if (i > 0 && nums[i] === nums[i - 1]) continue
        for (let j = i + 1; j < nums.length; j++) {
            // 第二个数去重
            if (j - i > 1 && nums[j] === nums[j - 1]) continue
            let left = j + 1
            let right = nums.length - 1
            while (left < right) {
                // 第三个数去重
                if (left - j > 1 && nums[left] === nums[left - 1]) {
                    left++
                    continue
                }
                // 第四个数去重
                if (right < nums.length - 1 && nums[right] === nums[right + 1]) {
                    right--
                    continue
                }
                const sum = nums[i] + nums[j] + nums[left] + nums[right]
                if (sum > target) {
                    right--
                } else if (sum < target) {
                    left++
                } else {
                    res.push([nums[i], nums[j], nums[left], nums[right]])
                    left++
                    right--
                }
            }
        }
    }
    return res
};

3、力扣 26 题:删除有序数组中的重复项

 

 

 本题仍然可以使用双指针法。由于数组是升序排列的,所以我们可以维护一个快指针 right,负责检查元素是否和上一个元素重复,维护一个慢指针 left,用来储存不重复的元素。

  • 如果 right 指向的元素和上一个元素不重复,将 right 指向的元素储存到 left 指向的位置,快慢指针同时向右走一步;
  • 如果 right 指向的元素和上一个元素不重复,那么直接将 right 向右走一步,检查下一个元素

数组的第一个元素无论如何都不会重复,所以可以直接从第二个元素开始。 

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    let left = 1
    for (let right = 1; right < nums.length; right++) {
        if (nums[right] !== nums[right - 1]) {
            nums[left++] = nums[right]
        }
    }
    return left
};

4、力扣 27 题:移除元素

 

 

 此题跟上题思路一致,快指针用来检测值是否和 val 相等,慢指针则用来记录值不等于 val 的元素。但是此题需要从第一个元素开始检测。

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let left = 0
    for (let right = 0; right < nums.length; right++) {
        if (nums[right] !== val) {
            nums[left++] = nums[right]
        }
    }
    return left
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值