双指针技巧汇总(含滑动窗口)

本文将双指针分为快慢指针和左右指针两大类进行介绍。

目录

一、快慢指针

(1)判断链表中是否含有环 

代码实现 

(2)寻址链表的中点 

 代码演示

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

代码演示

二、左右指针

(1)二分查找

(2)反转数组

代码演示

(3)滑动窗口,下面详解 

三、滑动窗口算法

整体思路

leetcode例题讲解

(1)LeetCode 76 题 最小覆盖字串

(2)其它类似例题,思路都是一致的


 


一、快慢指针

 快慢指针一般都初始化指向链表的头结点 head,前进时快指针 fast 在前,慢指针 slow 在后。

(1)判断链表中是否含有环 

 算法思路:

1. 设置快指针fast、慢指针slow,然后让fast以2的步长,slow以1的步长进行遍历

2.如果链表中不含有环,fast指针最后就会遇到空指针就代表到头了

3. 如果链表含有环,快指针肯定会在某一个时刻超过慢指针一圈,最后相遇

      此时关于环有几点需要说明:

         1. 首先,此时slow走了k步  / fast 走了2k步,相遇点距离圈的切点BC等于m,

            那么如果要判断B点的位置就只需要让其中一个结点回到A起点,然后两者同速进行,相遇就是切点

            解释:BC = m,AB = k-m,  C转一圈到B = k - m

 

代码实现 

ListNode detectCycle(ListNode head){
    ListNode fast,slow;
    fast = slow = head;
    while(fast != null && fast.next != null){
        fast = fast.next.next;      //2的步长
        slow = slow.next;        //1的步长
        if(fast == slow) break;
    }

    //接下来是找圈的切点的位置
    slow = head; //先让一个结点回到起点
    while(fast != slow){
        fast = fast.next;
        slow = slow.next; //以相等步长前进
    }

    return slow;
}

 

(2)寻址链表的中点 

算法思路:

      依旧是fast以slow的两倍速进行遍历,当fast到链表结尾就代表slow到中点了。

奇偶分析:

      1.当长度为奇数时,slow正好在中点

      2. 当长度为偶数时,slow在中偏右一个节点

 代码演示

ListNode slow,fast;
slow = fast = head;
while (fast != nu1l && fast.next != nu1l) {
    fast = fast. next.next;
    slow = slow. next;
}
    // slow就在中间位置
    return slow;

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

思路和上面类似,就不再赘述。

代码演示

ListNode slow, fast;
slow = fast = head;
while (k-- > 0)
    fast = fast . next;
    while (fast != nu1l) {
        slow = slow. next; 
        fast = fast. next;
    }
return slow;

二、左右指针

(1)二分查找

算法思路不再赘述,可以从我的二分查找算法一锅端学习。

(2)反转数组

算法思路:

       一左一右进行遍历并交换,直到数组中点。

代码演示

void reverse(int[ ] nums) {
    int left = 0;
    int right = nums.length -1;
    while (left < right) {
        // swap(nums[left], nums[right])
        int temp = nums [left];
        nums[left] = nums[right];
        nums[right] = temp;
        left++; right--;
    }
}

(3)滑动窗口,下面详解 


三、滑动窗口算法

整体思路

int left = 0, right = 0;

while (right < s.size()) {
    // 增大窗口
    window.add(s[right]);
    right++;

    while (window needs shrink) {
        // 缩小窗口
        window.remove(s[left]);
        left++;
    }
}

leetcode例题讲解

(1)LeetCode 76 题 最小覆盖字串

 思路:

1、我们在字符串S中使用双指针中的左右指针技巧,初始化left = right = 0,把索引左闭右开区间[left, right)称为一个「窗口」。

2、我们先不断地增加right指针扩大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。

3、此时,我们停止增加right,转而不断增加left指针缩小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了)。同时,每次增加left,我们都要更新一轮结果。

4、重复第 2 和第 3 步,直到right到达字符串S的尽头

实际代码

    public String minWindow(String s, String t) {
        HashMap<Character,Integer> need = new HashMap();
        HashMap<Character,Integer> window = new HashMap();
        int left = 0,right = 0;
        Character temp = null;
        for(int i = 0; i < t.length(); i++){ //初始化need
            need.put(t.charAt(i),need.getOrDefault(t.charAt(i),0) + 1);
        }
        int count = 0;//记录window中符合条件的字符数
        int maxLen = Integer.MAX_VALUE;//记录最小字串的长度
        int start = 0;
        int end = 0;//记录最小字串的开始和结束
        while(right < s.length()){
            //取出字符
            temp = s.charAt(right);
            window.put(temp,window.getOrDefault(temp,0) + 1);
            if(need.containsKey(temp)){
                //在windw中添加,累积出现次数
                
                //判断此时window中的字符是否完全包含了target
                //如果right所指的字符在target和window中含有相同个数,count++
                if(window.get(temp).compareTo(need.get(temp)) == 0){
                    count++;
                }
            }
            right++;
            while(count == need.size()){
                //满足情况,需要更新结果
                if((right - left) < maxLen){
                    start = left;
                    end = right;
                    maxLen = right - start;
                }
                //开始优化
                char c = s.charAt(left);
                if(need.containsKey(c)){   //如果当前字符包含target中,就更新window出现的值
                    window.put(c,window.getOrDefault(c,1) - 1);
                    if(window.get(c) < need.get(c)){
                        count--;
                    }
                }
                left++;
            }
        }
        return maxLen == Integer.MAX_VALUE ? "" : s.substring(start,end);
    }

总结难点,易错点

  1.  maxLen 要初始化为 Integer.MAX_VALUE
  2. 只要是字符就要加入到window中,但是只有同时属于target的字符,才需要进行判断是否满足target中的个数,如果满足要进行count++;
  3. 优化时,通过count数减小来退出循环
  4. 进行比较时,要使用compareTo

(2)其它类似例题,思路都是一致的

  • LeetCode 567 题  --- 字符串排列

  • LeetCode 第 438 题 --- 找所有字母异位词

  • LeetCode 第 3 题 --- 最长无重复子串

相信大家对双指针的使用应该都没啥问题了,最后几道题目有兴趣的可以自行去leetcode进行练习!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值