解决算法问题常用的一种方法,前后指针法

前后指针法是  解决很多算法问题当中经常会出现的一种解决方法,也被称为是滑动窗口解法。

很多人在遇到这种问题的时候 ,可能连答案,题解什么的都懒得推敲和总结就放弃了,其实这个方法学会之后,你可以举一反三的使用在很多的算法题解题当中去。今天我们就来看看这种方法与模式。

滑动窗口解法,顾名思义,是用来解决顺序数组列表问题的,因为在算法运行过程当中,滑动窗口是一个不会断开的整体,所以一定是用于解决顺序的,甚至是排序之和 的那种数组啊,列表啊之类的问题。

举个例子,leetcode中无重复字符的最长字串的长度。

abcabcbb,这个就可以用到滑动窗口的解决办法,因为这就是一个需要顺序的,连续的数组长度作为最后结果的这么一道题。

对于滑动窗口咱们如何去用代码实现,从理解上可以使用两个 指针,一前一后。

var lengthOfLongestSubstring = function (s) {

      let n = s.length;

     let set = new Set();

     let right = -1;

    let max = 0;

      for (let  i=0;i<s.length;i++) {

            if (i!=0) {

               set.delete(s.charAt(i-1));  // i 只要不等于0就会从set中delete掉一个最前面的值。

            }

           while (right + 1 < n && !set.has(s.charAt(right + 1))) {

               set.add(s.charAt(right+1));

                ++right;

           }

          max = Math.max(max,right - i + 1);

      }

     return max;

}

以上这段代码是官方给出的一个题解,但是仔细探究之后会发现,这个滑动窗口性能太差,为什么?

按道理来讲,外层for循环,进去之后,索引i 的 角色就成了  后 指针,咱们首先不管if(i!=0){} 这个里面的逻辑,先看while()里面的 rsk一开始在运行外部赋值为 -1,为什么??? rsk在这段代码中的含义就是 right,前指针,从for循环开始 i = 0, 一进入while 基本上 如果遇到 输入的测试字符串是abcdef  就出不来了,会一直到 f 才从while循环中出来,因为abcdef中没有相同的字母,

rsk+1<n && !set.has(s.charAt(rsk+1))的判断也就一直成立,rsk角色就是这个滑动窗口前指针,而且它还有一个角色,就是while循环的循环索引,++rsk,while循环就循环着,出了while循环,max就用来记录这个滑动窗口中,字符不一样的组成的那个字符串的长度,而且是最长的长度,因为每次都需要做一个比较 max = Math.max(max,rsk-i+1); 就是将之前的max的值与新构成的滑动窗口的字符串长度做比较,记录最长长度,

这里感觉最别扭的就是if()那段逻辑中的delete方法了。

if (i!=0) set.delete(s.charAt(i-1));  只要从while循环里面出来,再次走进for循环,就会进入这段删除逻辑,那么这段删除逻辑是为了干啥呢?咱们走一个过程来看就清楚了。

第一步   (a)bcabcbb,进入while,没执行 delete,      记录最大值max为1

第二步     (ab)cabcbb,依然在while,没执行delete,    记录最大值max为2

第三步     (abc)abcbb,   while最后一次,没执行delete,记录最大值max为3

第四步     a(bca)bcbb, while第二次进入,执行了delete,前指针 i 后移(因为for循环的 i++ 了),

第五步     a(bca)bcbb, 遇到了新的b,再次出了while循环,记录最大值 max为3

第六步     ab(cab)cbb,执行delete删除了b,i又再次因为for循环++了,while循环却又遇到了新的c。 记录最大值 max为3

第七步     abc(abc)bb,执行delete删除了c,i又再次因为for循环++了,while循环却又遇到了新的b, 记录最大值 max为3

第八步     abc[a(bc)b]b,此时delete的劣势显现了出来,也就是咱们遇到的abcb类型,a先出来,但是这一次执行delete之后,没有进入while循环,而是a(bc)b此时要等b从括号中delete出来,ab(c)b ,这个b才能进入括号中去 ab(cb)。

到最后  (cb)b 结尾。c先出来,c(b)b,然后b再出来,cb(b) 这样才算。

这个滑动窗口虽然让咱们学习到了,可以通过两层循环去控制一个滑动窗口的移动,但是,他的这个滑动窗口,是前面的那个指针需要刻意等待 后面那个指针跟上来,所以这个滑动窗口需要改进。

var lengthOfLongestSubstring = function (s) {

      let obj = {};

      let  max = 0;

      let left = 0;

      for (let i=0;i<s.length;i++) {

          if (obj.hasOwnProperty(s.charAt(i))) {

              left = Math.max(left, obj[s.charAt(i)] + 1);

          }

          obj[s.charAt(i)] = i;

          max = Math.max(max,i - left + 1);

      }

     return max;

}

这个就是改进的滑动窗口,后面的指针,也就是左边的指针,不会一步一步的走,让right指针等待,而是一步就跨越过去

(a)bcabcbb   

(ab)cabcbb 

(abc)abcbb   

a(bca)bcbb 

ab(cab)cbb 

abc(abc)bb 

abcab(cb)b    这里遇到abcb的类型的时候, (abc)b,左指针无需等待,直接跳段到c,ab(cb)。

这就是改进之后的滑动窗口。

是不是跟  教科书  里面 的滑动窗口  和 改进之和的滑动窗口 一模一样

前者用两层循环,for中 嵌套while,一个set数据结构搞定,

后者用一层for循环,一个 对象obj搞定。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luolvzhou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值