【LeetCode】双指针 & 滑动窗口

一、双指针简介

双指针是指在算法中同时使用两个指针来追踪数组或序列中的元素位置。这两个指针可以朝着相同方向移动,也可以朝着相反方向移动。
在算法问题中,双指针通常用于在数组、链表或其他数据结构中执行一些特定的操作,如搜索、遍历、或找出满足条件的子序列等。

双指针模式

1、快慢指针: 两个指针以不同的速度移动。快指针可能每次移动两步,而慢指针每次移动一步。这种模式常用于检测循环或链表中的环。

2、左右指针: 两个指针分别从数组的两端开始移动,逐渐向中间靠拢。这种模式常用于在有序数组中查找特定的元素,或解决一些数组中的问题。

3、滑动窗口: 两个指针构成一个窗口,通过移动窗口来执行某种操作。这种模式常用于解决子数组或子字符串的问题。

使用双指针的好处在于可以在不使用额外空间的情况下,以较高的效率解决一些问题。在一些情况下,双指针也可以降低算法的时间复杂度。

需要注意的是,不同的问题可能需要不同类型的双指针,因此在应用双指针模式时,需要根据具体情况选择合适的策略。

使用双指针来解决的问题

在解决算法问题时,能够使用双指针的问题通常具有一些特征,这些特征可能暗示着双指针方法可能是有效的。以下是一些常见的特征,可以帮助你判断一道问题是否可以用双指针来解决:

1、有序数组或链表: 当问题涉及到有序数组或链表时,双指针通常是一个不错的选择。在这种情况下,可以使用双指针来在数组或链表中进行搜索、查找或执行其他操作。

2、滑动窗口问题: 如果问题涉及到窗口的概念,比如求解子数组或子字符串的问题,那么双指针可能是一个合适的选择。滑动窗口问题通常可以使用两个指针来表示窗口的起始和结束。

3、夹逼法: 当问题可以通过夹逼法来解决时,双指针也是常见的选择。夹逼法通常在数组中查找满足一些条件的两个元素时使用,例如在有序数组中查找两个元素的和等于目标值。

4、反转问题: 一些反转类的问题,尤其是链表的反转问题,常常可以使用双指针来解决。

5、快慢指针: 在涉及到环或链表中的一些特殊结构时,快慢指针通常是一个有用的技巧。比如,判断链表是否有环,或者找到环的起点等。

归并操作: 在某些情况下,双指针可以用于归并操作,例如合并两个有序数组。

题目

移动0
盛最多水的容器
接雨水

思路:

  1. 设置左右指针、左边最大高度和右边最大高度
  2. 遍历数组,当左指针小于右指针的时候,执行下面步骤
    • 如果左边高度小于右边高度,说明左侧高度低
      • 检查当前柱子的高度是否大于等于左边最大高度
      • 如果是,更新左边最大高度
      • 如果不是,计算积水量,并且累加到总计中,然后左指针右移
    • 如果左边高度大于等于右边高度,说明右侧高度低
      • 检查当前柱子的高度是否大于右边最大高度
      • 如果是,更新右边最大高度
      • 如果不是,计算积水量,并且累加到总结中,然后右指针左移
// 接雨水
/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {

};

二、滑动窗口简介

是什么:通过一个左右指针维护一个窗口,确定窗口需要满足的条件。在滑动窗口的过程中,根据题目的要求记录解或更新最优解

基本步骤
1、初始化:定义两个指针,左指针(start)和右指针(end),表示窗口的开始和结束位置
2、滑动窗口:开始遍历数组或列标,移动右指针或者左指针,调整窗口大小
3、记录结果:在滑动过程中,记录解或者更新最优解
4、窗口的条件:根据问题的要求,确定窗口需要满足的条件,通常是某个区间的和、平均值、最大最小值
5、结束条件:确定何时停止滑动窗口,通常是右指针到达数组的末尾

题目

最小子串

思路:

  1. 初始化左指针为0,标记窗口的开始位置
  2. 初始化最大子串长度为0,记录最长子串长度
  3. 初始化字符到索引的映射charIndexMap,用于存储每个字符最后一次出现的位置
  4. 遍历字符串,右指针从0开始向右移动
  5. 对于每个字符,检查是否出现在当前窗口
    • 如果出现过,移动左指针到重复字符的下一个位置,确保窗口中的字符不是重复的
    • 更新字符到索引的映射charIndexMap
    • 更新最大子串长度
  6. 直到循环结束
   // 初始化左指针、最大子串长度、字符到索引的映射
    let left = 0;
    let maxLength = 0;
    const charIndexMap = {};

    // 遍历字符串
    for (let right = 0; right < s.length; right++) {
        const currentChar = s[right];

        // 如果当前字符已经在窗口中出现过
        if (charIndexMap[currentChar] !== undefined && charIndexMap[currentChar] >= left) {
            // 移动左指针到重复字符的下一个位置
            left = charIndexMap[currentChar] + 1;
        }

        // 更新字符到索引的映射
        charIndexMap[currentChar] = right;

        // 更新最大子串长度
        maxLength = Math.max(maxLength, right - left + 1);
    }

    // 返回最大子串长度
    return maxLength; 
    const charIndex = new Array(256).fill(-1); // ASCII 字符集
    let left = 0;
    let maxLength = 0;

    for (let right = 0; right < s.length; right++) {
        const currentChar = s[right];
        const charCode = currentChar.charCodeAt(0);

        left = Math.max(left, charIndex[charCode] + 1); // 更新左指针,确保它不会后退到已经遍历过的位置
        charIndex[charCode] = right; // 更新字符的最后出现位置

        maxLength = Math.max(maxLength, right - left + 1);
    }

    return maxLength;
找到字符串中所有字母异位词
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值