【算法】双指针思维

93 阅读2分钟

何为双指针?

双指针算法是一种利用两个指针在数据结构(如数组或链表)上协作遍历的策略。这种算法的核心在于通过两个指针的相对移动来扫描数据,从而达到诸如查找、删除、排序或优化遍历过程等目的。双指针主要分为以下两种常见模式:

1. 快慢指针

  • 快的不停移,慢的满足条件才移:【数组去重】【移动0】
1)去重:[0, slow]范围内去除val
  let slow = 0;
  for (let fast = 0; fast < nums.length; fast++) { // 快指针不停移动
    if (nums[fast] !== val) nums[slow++] = nums[fast]
  }
  • 快二慢一:【循环链表】
   while (fast.next) {
     fast = fast.next.next; 
     slow = slow.next
   };
  • 快的先移n,然后快慢一起移:【删除链表中的倒数第N个节点】

2. 对撞指针

  • 两指针从数组或序列的两端开始,向中间移动,直到它们相遇或超越某个条件。
  • 应用:数组中的目标和搜索、查找对称数、数组去重、实现滑动窗口。

模板

function fn(arr) {
   let left = 0,right = arr.length - 1;
   while(left < right) {
     // if( 满足条件 )   {return 结果}
     // if( 找到目标 )   {处理逻辑} 
           if( 偏小 )  left++
     else  if( 偏大 )  right--
   }
}
a. 接雨水

image.png

const height = [0,1,0,2,1,0,1,3,2,1,2,1] // 6
解释:图中蓝色区域是6

 leftMax, rightMax用来维护 左柱最高值、右柱最高值
 while (left < right) {
   分别更新 左右两边最高的柱子
 
   左边低?ans += 左边高度差;左移
   右边低?ans += 右边高度差;右移
 }
b. 盛水最多的容器

const height = [1,8,6,2,5,4,8,3,7] // 49
解释:在8和7两根柱子之间盛水最多=7 * (8-1)

while (left < right) {
  当前盛的水 = 左柱和右柱中最短的柱 * (左右柱距离)
  更新max
  
  左柱低?左移
  右柱低?右移
}
c. 三数之和

const nums = [-1,0,1,2,-1,-4] // [ [ -1, -1, 2 ], [ -1, 0, 1 ] ]
解释:所有可能的三个数字之和为0的组合

 * 先排序
 * for i 遍历 nums {
 *   去重:nums[i]==之前?continue
 *   while (left < right) {
 *     if(sum==0){
 *       ans.push([nums[i], nums[left], nums[right]])
 *       左移
 *       左移后去重:nums[left] ==之前?左移
 *       右移
 *       右移后去重:nums[right]==之前?右移
 *     }else if(小于0) 左移
 *      else if(大于0) 右移
 *   }
 * }

3. 二分搜索

  while (left <= right) {
      let mid = Math.floor((left + right) / 2)
      
      if (arr[mid] == target)     return mid
      else if (arr[mid] < target) left = mid + 1
      else                        right = mid - 1; 
  }

4. 滑动窗口

  • 无重复的最长字串
  • 找字符串中所有字母异位词
  • 最小覆盖字串

5. 前缀和

// (n)前缀和
// 区间[a,b]的和为sum :表示 map[a] - map[b] = sum
// map[i] = map[i-1] + nums[i]
// map[i+1] = map[i] + nums[i+1]
// curSum   =        + target
// 区间[j, i] 的和为k :表示 map[i] - map[j] = k