我正在参加「掘金·启航计划」
什么是双指针
双指针算法是指在数组中使用两个指针,从数组相同方向或者相反方向遍历数组,从而达到某种目的。
算法主要利用了数组有序的特点,根据遍历方向不同,可以分为左右指针和快慢指针(滑动窗口)两种类型。
这两种算法思想都可以优化程序的时间和空间复杂度,常用于数组、链表等数据结构的处理,本文主要介绍双指针在数组中的应用。
这里的指针,并非专指C中指针的概念,而是指索引,游标或指针,可迭代对象等
分类
左右指针
左右指针是指在数组中使用两个指针,分别指向数组的第一个元素和最后一个元素,根据题目的要求,通过移动指针的位置到中间,直到相遇,从而解决问题。
题目特点
每个循环,仅看数组中的2个元素,双指针意在找到符合条件的2个元素,求出对应的结果。
伪代码模板如下:
function fn (list) {
var left = 0;
var right = list.length - 1;
//遍历数组
while (left <= right) {
left++;
// 一些条件判断 和处理
... ...
right--;
}
}
上手试试
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
题解
var reverseString = function(s) {
let left = 0;
let right = s.length - 1;
while (left <= right) {
[s[left], s[right]] = [s[right], s[left]];
left++;
right--;
}
return s;
};
经典例题
快慢指针(滑动窗口)
快慢指针,也叫滑动窗口,它的思想是使用两个指针同时从同一个方向遍历某个数据结构,其中一个指针的移动速度较快,称为“快指针”,另一个指针的移动速度较慢,称为“慢指针”。
利用这种不同的移动速度,可以帮助我们解决一些复杂的问题。
通常情况下都是解决字符串匹配及字串问题,通过将字符串转换为数组,然后使用快慢指针操作,得到解。
题目特点
在每次循环中得到一个子区间,由两个指针框起来,通过判断区间是否满足条件,得到最优解。
伪代码模板如下:
function fn (list) {
var left = 0;
var right = 0;
//遍历数组
while (right < list.length) {
// 一些条件判断 和 处理
... ...
right++;
// 一些条件判断 和 处理
... ...
left++;
}
}
上手试试
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
题解
var lengthOfLongestSubstring = function(str) {
let n = str.length
// 滑动窗口为s[left...right]
let left = 0
let right = -1
let freqMap = {} // 记录当前子串中下标对应的出现频率
let max = 0 // 找到的满足条件子串的最长长度
while (left < n) {
let nextLetter = str[right + 1]
if (!freqMap[nextLetter] && nextLetter !== undefined) {
freqMap[nextLetter] = 1
right++
} else {
freqMap[str[left]] = 0
left++
}
max = Math.max(max, right - left + 1)
}
return max
};
经典例题
- 找到符合条件的子区间
- 子区间计数问题