「算法修炼」 Leetcode - 双指针 - 左右指针

169 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

左右指针

在有序数组中,将指向最左侧的索引定义为左指针(left),最右侧的定义为右指针(right),然后从两头向中间进行数组遍历。

左右指针主要用于解决数组(或者字符串)中的问题:

  • 二分查找:定义一左一右两个指针,然后向中间遍历,直到满足条件或者两个指针相遇。
  • 滑动窗口:两个指针,一前一后组成滑动窗口,并计算滑动窗口中的元素的问题。

左右指针的相关题目

题型1 - 二分查找

参考二分法 ☞ 传送门

题型2 - 滑动窗口

  1. 题目:

    209. 长度最小的子数组Medium:给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

  2. 思路:

    使用一层for循环,遍历窗口的终止位置,当终止位置移动时(即放大窗口将符合的目标集合包进窗口)到符合目标集合的位置时,开始移动起始位置,即缩小窗口,判断是否依旧满足≥target的要求,并存储最小。起始位置移动(窗口缩小)到第一个不满足≥target的窗口时,终止位置继续移动。知道循环结束,

  3. 代码:

// @lc code=start
/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function(target, nums) {
    let sum = 0     // 最小子数组的和
    // 最后的结果,子数组一定小于原数组长度,为了便于后面比较,这里取一个大于最大长度的值。
    let result = nums.length + 1  
    let subLen = 0 // 当前子数组长度
    // 滑动窗口
    let start = 0 // 起始位置
    for(let end = 0; end < nums.length; end++) {
        // 开始遍历,即移动窗口终止位置,放大窗口
        // 计算当前窗口内元素的和
        sum += nums[end]
        // 当sum >= target时 , 窗口内有符合要求的数组,存储子数组长度,并开始移动起始位置
        while(sum >= target){
            // 计算出此时子窗口内数组的长度
            subLen = end - start + 1
            // 比较此时子数组长度和result,取小
            result = result > subLen ? subLen : result
            // 子数组和减去当前起始位置的值
            sum  -= nums[start]
            // 开始移动窗口起始位置,即缩小窗口
            start++
        }
    }
    // 最后判断如果result > 原数组长度,则说明没有最小子数组返回长度0,反之返回result
    return result > nums.length ? 0 : result
};
// @lc code=end
  1. 复杂度分析

    • 时间复杂度:O(n)nnums数组的长度,只有一层for循环遍历了nums的长度。
    • 空间复杂度:O(1)。只使用了 sumresultsubLenstart 常数个变量。