算法思维 ---- 双指针法

双指针

双指针可分为三类:

  • 左右指针
  • 快慢指针
  • 滑动窗口

其中,滑动窗口 已单独写了一篇随笔,可跳转查看。

在快慢指针中,又有一类典型算法,叫 Floyd's cycle-finding algorithm,其又名 Floyd's Hare and Tortoise Algorithm

在这里,我们主要谈论 左右指针快慢指针。左右指针 主要应用在数组中,快慢指针 主要应用在数组和链表中。

左右指针

见名知意,左右指针就是指针在数组的两端向中间靠拢,典型的二分法使用的就是此类指针。

二分查找

const binarySearch = (nums, target) => {
  let left = 0, right = nums.length - 1;
  while (left <= right) {
    let mid = Math.floor((left + right) / 2);
    if (nums[mid] === target) return mid;
    else if (nums[mid] < target) left = mid + 1;
    else if (nums[mid] > target) right = mid - 1;
  }
  return -1;
}

反转数组

let reverse = (nums) => {
  let left = 0, right = nums.length - 1;

  while (left <= right) {
    [nums[left], nums[right]] = [nums[right], nums[left]];
    left++;
    right--;
  }
}

快慢指针

快慢指针中的一类典型算法已经写了随笔,可跳转看看。Floyd's cycle-finding Algorithm

快慢指针,其实就是有 fastslow 指针,它们以不同的速度前进,或者 fast 先行,满足一定条件后,slow 再前行。

链表中点

fast 指针一次两步,slow 指针一次一步,当 fast 指针到达链表尾时,slow 指针就处于链表的中间位置,或者是中间偏右的位置(偶数个结点)。

如果是找中间数或者删除中间结点时,我们可以加多一个变量来记录 slow 的上一步的结点,以便操作。

while (fast !== null && fast.next !== null) {
  fast = fast.next.next;
  slow = slow.next;
}

寻找链表的倒数第k个元素

这里是让 fast 指针先行 k 步,然后两指针同时同步前进。当 fast 指针到达指针末尾时,slow 指针就是倒数第k个元素了。

while (k-- > 0) {
  fast = fast.next;
}

while (fast !== null) {
  fast = fast.next;
  slow = slow.next;
}
// 此时 slow 就是倒数第k个元素

移除元素

我们让 fast 先行,如果找到不是指定移除的元素的元素,我们就让它替换到 slow 的位置,然后 slow 指针前进一步。

当操作完成后,slow 指针的位置就是操作后的数组的长度。下面算法主要展示的是移除数组元素的,移除链表元素也是同样的操作,不过最后的 slow 指针指向的结点的next域需要置空。

用快慢指针就可以在原数组或链表的基础上进行移除元素,当然这要看实际需求,如果不能操作原数据,则需要消耗O(n)空间复杂度。

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

总结

左右指针快慢指针 就是这么简单的操作,复杂的还是 滑动窗口,不过其也是有套路可循的。

算法,最重要的还是其背后的思维。把算法思维转化为代码也是很重要的。

还需很努力,很有很多算法没有掌握,输入消化吸收后也要对应输出。

posted @ 2020-11-26 10:13  浪荡&不羁  阅读(170)  评论(0编辑  收藏  举报