算法中的双指针应用,有时候会感觉很奇妙,解决了很多的问题,有必要演绎总结一下,首先双指针也是个很宽泛的概念,它相似于遍历中的 i 和 j
然而其区别是,两个指针是同时挪动的,即没有奉献复杂度从 O(N)
到 O(N*N)
,所以被很多算法大佬所推崇,所以基于此演绎总结出双指针的常见解法和套路。
1. 题型演绎
这里将题型演绎为三种,别离如下:
- 快慢指针(前后按不同步调的两个指针)
- 前后双端指针(一个在首,一个在尾部,向两头聚拢)
- 固定距离的指针(以 i, i + k 的间距的两个指针)
后面讲到,这三种指针都是遍历一次数组即可实现,其工夫复杂度低于或者等于 O(N)
,空间复杂度是 O(1)
因为就两个指针存储。
2. 常见题型
2.1 快慢指针
相当经典的一道题:
- 判断链表是否有环 -
通过步调不统一的两个指针,一前一后一起挪动,直到指针重合了
https://leetcode.com/problems/linked-list-cycle/description,代码片段如下:
public boolean hasCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (slow != null && fast != null) {
ListNode n = fast.next;
fast = n == null ? null : n.next;
if (slow == fast) {return true;}
slow = slow.next;
}
return false;
}
- 寻找反复复数,从数组中找出一个反复的整数:https://leetcode-cn.com/problems/find-the-duplicate-number/
代码解决如下:
public int findDuplicate(int[] nums) {
// 将其看成是一个循环的链表,快慢指针循环
int index1 = 0;
int index2 = 0;
do
{index1 = nums[index1];
index2 = nums[index2];
index2 = nums[index2];
}while (nums[index1] != nums[index2]);
index1 = 0;
// 找出在哪个地位为起始点,可证必然在圆圈终点相遇
while(index1 != index2){index1 = nums[index1];
index2 = nums[index2];
}
return index1;
}
2.2 前后首尾端点指针
- 二分查找
二分查找是典型的前后指针的题型,代码如下:
public static int binarySearch(int[] array, int targetElement) {int leftIndex = 0, rightIndex = array.length - 1, middleIndex = (leftIndex + rightIndex) / 2;
while(leftIndex <= rightIndex) {int middleElement = array[middleIndex];
if(targetElement < middleElement) {rightIndex = middleIndex - 1;}else if(targetElement > middleElement) {leftIndex = middleIndex + 1;}else {return middleIndex;}
middleIndex = (leftIndex + rightIndex) / 2;
}
return -1;
}
2.3 固定距离的指针
- 求链表中点 https://leetcode-cn.com/problems/middle-of-the-linked-list/
// 快指针 q 每次走 2 步,慢指针 p 每次走 1 步,当 q 走到开端时 p 正好走到两头。class Solution {public ListNode middleNode(ListNode head) {
ListNode p = head, q = head;
while (q != null && q.next != null) {
q = q.next.next;
p = p.next;
}
return p;
}
}
- 求链表倒数第 k 个元素 https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/
// 快慢指针,先让快指针走 k 步,而后两个指针同步走,当快指针走到头时,慢指针就是链表倒数第 k 个节点。public ListNode getKthFromEnd(ListNode head, int k) {
ListNode frontNode = head, behindNode = head;
while (frontNode != null && k > 0) {
frontNode = frontNode.next;
k--;
}
while (frontNode != null) {
frontNode = frontNode.next;
behindNode = behindNode.next;
}
return behindNode;
}
3. 模板总结
看完三个代码,是不是感觉很简略,上面总结一下三种双指针的代码模板
// 1. 快慢指针
l = 0
r = 0
while 没有遍历完
if 肯定条件
l += 1
r += 1
return 适合的值
//2. 左右端点指针
l = 0
r = n - 1
while l < r
if 找到了
return 找到的值
if 肯定条件 1
l += 1
else if 肯定条件 2
r -= 1
return 没找到
//3. 固定间距指针
l = 0
r = k
while 没有遍历完
自定义逻辑
l += 1
r += 1
return 适合的值
吴邪,小三爷,混迹于后盾,大数据,人工智能畛域的小菜鸟。
更多请关注