LeetCode热题100双指针题解析

107 阅读4分钟

难度标识:⭐:简单,⭐⭐:中等,⭐⭐⭐:困难

tips:这里的难度不是根据LeetCode难度定义的,而是根据我解题之后体验到题目的复杂度定义的。

1.移动零

1.思路

这题可以使用快慢指针,使用快指针遍历整个数组,快指针找到不是0的值,就交换 nums[left]nums[right] 的位置。当 fast 指针到达数组的末尾时,所有的0都已经移动到了数组的右边。

2.代码

var moveZeroes = function (nums) {
    let slow = 0, fast = 0;
    while (fast < nums.length) {
        if (nums[fast] !== 0) {
            [nums[slow], nums[fast]] = [nums[fast], nums[slow]]
            slow++
        }
        fast++
    }
    return nums
};

2.盛最多水的容器

1.思路

这题也非常的简单,可以使用左右指针

(1)使用两个指针,一个开始时指向数组的第一个元素,另一个指向数组的最后一个元素。这意味着开始时宽度是最大的。

(2)计算两指针指向的线之间的面积:这是当前宽度和两条线中较短的那条的高度的乘积。

(3)为了尝试找到更大的面积,你应该移动较短的那条线的指针。原因是,如果移动较长的那条线,无论如何都会得到更小的面积(因为宽度减少了,而高度不可能增加)。

(4)重复步骤2和3,直到两个指针相遇。

2.代码

var maxArea = function (height) {
    let left = 0, right = height.length - 1;
    let maxVal = 0;
    while (left < right) {
        const minHeight = Math.min(height[left], height[right])
        const area = minHeight * (right - left)
        maxVal = Math.max(maxVal, area)
        if (height[left] < height[right]) {
            left++
        } else {
            right--
        }
    }
    return maxVal
};

3.三数之和 ⭐⭐

1.思路

解决这个问题的一个常见方法是先对数组进行排序,然后使用两个指针方法,这里也是属于左右指针

解决这个问题的一个常见方法是先对数组进行排序,然后使用两个指针方法。以下是解决该问题的详细步骤:

(1)排序数组:首先对数组进行排序。这使得在搜索可能的三元组时更容易避免重复。

(2)固定一个数,然后用双指针扫描

  • 1.遍历数组中的每一个数 nums[i]。对于每一个固定的 nums[i],使用两个指针leftright,分别初始化为 i+1nums.length-1

  • 2.如果 nums[i] + nums[left] + nums[right] < 0,那么增加 left 的值(向右移动),因为数组已排序,我们需要更大的数来尝试接近0。

  • 3.如果 nums[i] + nums[left] + nums[right] > 0,那么减小 right 的值(向左移动)。

  • 4.如果三数之和为0,则记录这个三元组。然后为了避免重复,leftright 都需要移动到下一个不同的数。

(3)跳过重复的数字:在整个过程中,为了避免记录重复的三元组,每次遇到重复的数字都要跳过。

2.代码

var threeSum = function (nums) {
    nums.sort((a, b) => a - b)
    const res = []
    for (let i = 0; i < nums.length - 2; i++) {
        if (i > 0 && nums[i] === nums[i - 1]) continue
        let left = i + 1, right = nums.length - 1
        while (left < right) {
            const sum = nums[i] + nums[left] + nums[right]
            if (sum === 0) {
                res.push([nums[i], nums[left], nums[right]])
                while (left < right && nums[left] === nums[left + 1]) left++
                while (left < right && nums[right] === nums[right - 1]) right--
                left++
                right--
            } else if (sum < 0) {
                left++
            } else {
                right--
            }
        }
    }
    return res
};

4.接雨水 ⭐⭐

1.思路

大名鼎鼎的接雨水问题,其实用左右双指针来解很简单。

(1)每个柱子能够接收的雨水量是由其左侧的最大高度和右侧的最大高度中的较小者决定的。换句话说,该位置的雨水量是受到左右两边界的限制的。

(2)从数组的两端开始,我们总是选择较矮的那个柱子来处理。原因是,较矮的柱子决定了当前能够接收的雨水量,而较高的柱子在此时并不重要。为了确保我们总是处理较矮的柱子,我们比较两个指针指向的柱子的高度,并移动较矮的那个。

(3)也就是说我们拿两个指针从前后一起遍历数组,比较哪个值小,然后去算小的那个接的雨水量并加到数组中。

2.代码

var trap = function (height) {
    let left = 0, right = height.length - 1, res = 0;
    let leftMax = 0, rightMax = 0;
    while (left < right) {
        if (height[left] < height[right]) {
            leftMax = Math.max(leftMax, height[left])
            res += leftMax - height[left]
            left++
        } else {
            rightMax = Math.max(rightMax, height[right])
            res += rightMax - height[right]
            right--
        }
    }
    return res
};

这题只要想明白了,用双指针的写法其实很简单,抓住核心点,一个柱子怎样才会有雨水,那就是在左边的时候,它右边的柱子比它底它就能接到雨水,在右边的时候,它左边的柱子比它低它就能接到雨水,所以用双指针的方法能算出每根柱子接雨水的多少。

双指针的hot 100的4题完结,总体而言,只要掌握了快慢指针和左右指针的用法,相对而言,还是挺简单的。