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

快慢指针:

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++;
            // 进行窗口内数据的一系列更新
            ...
        }
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值