快慢指针:
1、判定链表中是否含有环
用两个指针,一个跑得快,一个跑得慢。如果不含有环,跑得快的那个指针最终会遇到null,说明链表不含环;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。
注意:初始条件不能同时跨越两个指针,容易空指针异常
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){ //有环
return true
}
}
return false;
}
}
2、已知链表中含有环,返回这个环的起始位置
当快慢指针相遇时,让其中任一个指针指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){ //有环
slow = head;
while(slow != fast){ //找环起始位置
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
}
3、寻找链表的倒数第n
个元素
快慢指针,让快指针先走n
步,然后快慢指针开始同速前进。这样当快指针走到链表末尾null
时,慢指针所在的位置就是倒数第n
个链表节点(n
不会超过链表长度)。
LeetCode双指针例题
27(easy)移除元素
左右指针:
二分查找
滑动窗口:
①使用滑动窗口解题的场景:问题本身和能 单调性 建立关系,窗口内就是解决单调性的。
②看到题目能跟单调性扯上关系,那么立即想到滑动窗口、首尾指针法、单调栈、单调队列。
③滑动窗口的实现可以基于双端队列,也可以基于左右指针,优先考虑左右指针更节省空间,但是如果需要对窗口中的元素进行一定的处理操作,那么选择双端队列实现。左右指针是通过左指针代表左边界,右指针代表右边界,二者同时向右移动的基础原理。双端队列是左端点出数,右端点进数,不断的将元素入队和出队来实现的。
④单调队列的实现流程:pop和push操作要保持如下规则,pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作。push(value):如果push的元素value大于入口元素的数值,那么就将队列出口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止。
核心逻辑:
int left = 0, right = 0;
while (right < s.size()) {`
// 增大窗口
window.add(s[right]);
right++;
while (window needs shrink) {
// 记录结果数值
ans = Math.min(ans, window);
// 缩小窗口
window.remove(s[left++]);
left++;
}
}
return ans;
右移和左移窗口的更新操作,一般是对称的。
思考流程:
1、当移动 right 扩大窗口,即加入字符时,应该更新哪些数据?
2、什么条件下,窗口应该暂停扩大,开始移动 left 缩小窗口?
3、当移动 left 缩小窗口,即移出字符时,应该更新哪些数据?
4、我们要的结果应该在扩大窗口时还是缩小窗口时进行更新?
LeetCode滑动窗口例题:
209(medium) 长度最小的子数组
76(hard)最小覆盖子串
567(medium)字符串的排列
438(medium)找到字符串中所有字母异位词
3(medium)无重复字符的最长子串
42(hard)接雨水
/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
...
/*** debug 输出的位置 ***/
printf("window: [%d, %d)\n", left, right);
/********************/
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}