算法思维 ---- 双指针法
双指针
双指针可分为三类:
- 左右指针
- 快慢指针
- 滑动窗口
其中,滑动窗口 已单独写了一篇随笔,可跳转查看。
在快慢指针中,又有一类典型算法,叫 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
快慢指针,其实就是有 fast、slow 指针,它们以不同的速度前进,或者 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++;
}
总结
左右指针,快慢指针 就是这么简单的操作,复杂的还是 滑动窗口,不过其也是有套路可循的。
算法,最重要的还是其背后的思维。把算法思维转化为代码也是很重要的。
还需很努力,很有很多算法没有掌握,输入消化吸收后也要对应输出。