浅析双指针滑动窗口

所谓滑动窗口,就是由两个指针(左右,前后,快慢等待名称)夹住的区间。通常用于链表问题。

一、最简单的定长滑动窗口

先从生活中的实际例子开始。100个人从左到右坐成一排,我们不会叫第98个人,而是会叫倒数第二,而我们知道他是倒数第二,是因为我们反过来数了,那么如果不能反过来数,你怎么就知道他是倒数第二呢?

问题:单链表,找出倒数第K个元素。假设k小于链表长度。

如上图所示,绿色方块就是滑动窗口,左右分别代表第0个 和第k个。他们始终保持着距离一致向右滑动。当右指针走到了尽头时,左指针指向倒数第k个。

二、可变滑动窗口

问题:单链表,找出中间元素

可能第一反应就是,先遍历一遍得出长度,然后让头指针走长度的一半。no!我们是在讲滑动窗口!

从结果倒推。当右指针走到结尾的时候,左指针要在中间。容易想到,每一步,右指针应该比左指针多走一步。也就是说这个滑动窗口,一开始左边等于右边。每一步,都让右边走两步,左边走一步。当右边走不动的时候(只剩一步,或者0步都),左边指针就在中间位置。这是一个变化的滑动窗口

三、带条件的滑动窗口

问题:排序单链表,删除所有重复节点,只保留未重复出现的节点。

example:1>2>3>3>4>4>5.     输出。1>2>5

滑动窗口的左边应该是新链表的当前位置,而右边是每次出现重复的最后一个元素的下一个元素。

public class ListNode {
      public int val;
      public ListNode next;
      public ListNode(int x) { val = x; }
}

思路:每次碰到重复的(节点值 跟下个节点值一样),将右指针滑动到最后一个重复的位置,这部分需要内部循环一次,然后把左指针滑动到新的未重复的位置,右指针本次循环继续右滑。

否则,将左右指针各自右移,并且给链表赋值。

核心循环部分代码看起来是这样的:


        ListNode l = head;
        ListNode r = head;
        while(r != null){
            if(r.next != null && r.val == r.next.val){
                while(r.next != null && r.val == r.next.val){
                    r = r.next;
                }

                l.next = r.next;
                r = r.next;
            }
            else{
                l = l.next;
                r = r.next;
            }
        }

然后发现,循环走完了,左指针变成了链表的最后一项。但是要返回的是头部指针,因此需要引入哨兵(shaobing  简称sb),保存头部指针的引用,避免第一次赋值为空的判断。代码看起来是这样


        ListNode sb = new ListNode(2222);
        sb.next = head;
        ListNode l = sb;
        ListNode r = head;

        while(r != null){
            if(r.next != null && r.val == r.next.val){
                while(r.next != null && r.val == r.next.val){
                    r = r.next;
                }

                l.next = r.next;
                r = r.next;
            }
            else{
                l = l.next;
                r = r.next;
            }
        }

然后还发现一个地方,就是内循环的 while条件和 if判断重复了。可以用 do - while 来减少一次判断。

改完后是这样的



        ListNode sb = new ListNode(2222);
        sb.next = head;
        ListNode l = sb;
        ListNode r = head;

        while(r != null){
            if(r.next != null && r.val == r.next.val){
                do{
                    r = r.next;
                }while(r.next != null && r.val == r.next.val);

                l.next = r.next;
                r = r.next;
            }
            else{
                l = l.next;
                r = r.next;
            }
        }
        return sb.next;

滑动窗口关键是要搞清楚每次循环,指针应该要干些啥。在本题中,遇到重复的节点,两个节点都要滑到下一个不重复的节点上去,并且给链表赋值,如果不重复,那么各自右滑+1.

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值