【leetcode】数组类——滑动窗口、二分查找、快慢双指针


一味的做题是没有效果的,要学会多总结多思考,形成自己的模板,拿到题就知道用什么方法去解决出来。数组类的题目还是属于比较基础的,下面进行分类总结。

滑动窗口法

lc209长度最小的子数组(基础)

这道题是最基本的滑动窗口的做法,将指针内的元素相加,若大于等于target的时候记录长度,并移动left;小于的时候扩张,增加right

/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function(target, nums) {
    let len = nums.length+1;
    //利用滑动窗口来做;
    let left = 0;
    let right = 0;
    let sum = nums[0];
    while(right < nums.length){
        if(sum < target){
            right++;
            sum += nums[right];
        }
			while(sum >= target) {
	            len = Math.min(len,right-left+1);
	            sum -= nums[left];
	            left++;
        } 
    }
    return len > nums.length ? 0:len;
};

此题目是寻找最小窗口,所以满足条件后,进行缩小

lc76最小覆盖子串(困难)

这道题看起来就稍微复杂了,实际上还是只是在判断这个窗口的条件时后稍微复杂,但还不算很难

记录字符串中,有多少种类型,然后每种有多少个,为0的时候是一个判断类型的条件

var minWindow = function(s, t) {

    //let res = s.length+1;
    let ans = '';

    //t子串中的所有字符统计出来,几种类型,每种有多少个;

    let need = new Map();//需要的各种字符,及个数
    let needType = 0;//需要几种类型

    for(let item of t){
        if(need.has(item)){
            need.set(item,need.get(item)+1);
        }else{
            need.set(item,1);
        }
    }

    needType = need.size;


    //下面接着用滑动窗口进行判断,条件是k=0;只有某个key的值减为0才能为0;

    let right = 0;
    let left = 0;
    let str = '';

    while(right < s.length){
       // str += s[right];

        let char = s[right];

        if(need.has(char)){  
        need.set(char,need.get(char)-1);
        if(need.get(char) === 0){
            needType--;
        }
    }
        while(needType === 0){
            str = s.substring(left,right+1);
            //let len = right - left +1;
            if(!ans || str.length < ans.length){
                ans = str;
            }
            //说明各个字符都已经出现过,可以开始收缩了
            let ch = s[left];
            if(need.has(ch) ){
                need.set(ch,need.get(ch)+1);
                if(need.get(ch) === 1){
                needType++;
            }  
            }
           left++; 
        }
        right++; 
    }
return ans;
};

此题也属于,寻找最小窗口,中心思想还是,在满足条件后进行窗口的缩小操作;

lc904水果成篮(中等)

这道题和之前不太一样的地方在,它是寻找最大的窗口,不过中心思想就是不满足条件是,进行最大窗口的寻找,记录一下位置

var totalFruit = function(fruits) {
    if(fruits.length === 1 || fruits.length === 2){
        return fruits.length;
    }

    let left = 0;
    let right = 0;

    let lf = fruits[0];
    let rf = fruits[0];

    let res = 0;

    for(;right < fruits.length; right++){
        if(fruits[right] != rf && fruits[right] != lf){
            left = right - 1;
            while(left >= 1 && fruits[left-1] == fruits[left]){
                left--;
            }

            lf = fruits[left];
            rf = fruits[right]
        }
            res =Math.max(res,right-left+1);
    }
    return Math.max(2,res);
};

lc1004 最大连续个数1(中等)

此题也是寻找最大窗口,再做完水果成篮子后能够很简单的做出来

var longestOnes = function(nums, k) {
    //此题还是用滑动窗口来做
    //记录0的位置,当k+1个0出现的时候,就要进行位置变化

    //选择位置最近的两个0作为翻转

    let  stack = [];
    let len = 0;

    let right = 0;
    let left = 0;

    while(right < nums.length){
        if(nums[right] === 1){
            len = Math.max(len, right - left + 1 );
             right++;
            continue;
        }else{

            if(k== 0){
                left = right+1;
                right++;
                continue;
            }

            if( k && stack.length < k){
                stack.push(right);
            }else{
                //第k+1个0出现了
                left = stack.shift()+1 ;
                stack.push(right);
            }

            len = Math.max(len,right-left + 1 );
            right++;
        }
    }
    return len;

};

lc3 无重复字符的最长子串(中等)

这个也是一个简单的滑动窗口;

var lengthOfLongestSubstring = function(s) {
    //依旧可以用滑动窗口来做;
    //如果str出现重复的,就要把指针指到重复字符的下一个
    let len = 0;

    let left = 0;
    let right = 0;
    let str = '';

    while(right < s.length){
        let char = s[right];
        str = s.slice(left,right)
        if(str.includes(char)){
           
           while(s[left] != char){
               left++;
           }
           left ++;
        }
        len = Math.max(len,right-left + 1);
        right++;
    }
    return len;
};

总结

滑动窗口,无非分为两大类,一是寻找满足条件的最小窗口的长度,或者是寻找最大窗口的长度。这样滑动窗口的问题大部分都能解决了

二分查找

二分查找也是数组类型中经常爱考到的题,思路很简单,但边界条件往往容易理不清楚,这里把这类问题拉出来,好好总结一下。

正如随想录所说,常见的问题都出现于
例如到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1呢?

在二分查找题中,要确定循环不变量的规则,就是定义好区间,一般是

  • 左闭合右闭合,left == right 有效,right = mid - 1; left = mid + 1;
  • 左闭合右开, left < right right = mid; left = mid + 1;

lc34 搜索插入位置(简单)

var searchInsert = function(nums, target) {
    //左闭右闭
    let left = 0;
    let right = nums.length - 1;
    let position = -1;
    while(left <= right){
        let mid = Math.floor((left + right)/2);
        if(nums[mid] === target){
            position = mid;
            break;
        }
        if(nums[mid] < target){
            left = mid + 1;
        }else{
            right = mid - 1;
        }
    }
   return  position === -1 ? left  : position;
};

lc34 在排序数组中查找元素的第一个和最后一个位置(中等)

利用二分法,先查找出第一个大于target的,在查找第一个小于target,两次二分查找

var searchRange = function(nums, target) {
    //二分法查找三个8,一个范围;

   //先查找到第一个大于target的
    let ans = [-1,-1];
   let left = 0;
   let right = nums.length-1;

   while(left <= right){
       let mid = Math.floor((left + right)/2);

       if(nums[mid] === target && (mid === nums.length - 1 || nums[mid+1] > target)){
           ans[1] = mid;
           break;
       }

//左闭合右开?
        if(nums[mid] <= target){
            left = mid+1;
        }else{
            right = mid-1;
        }


   }


   //寻找左边界
   left = 0;

   while(left <= right){
        let m = Math.floor((left + right)/2);

        if(nums[m] === target && (m === 0 || nums[m-1] < target)){
            ans[0] = m;
            break;
        }

        if(nums[m] < target){
            left = m + 1;
        }else{
            right = m - 1;
        }
   }

   return (ans[0] === -1 || ans[1] === -1) ? [-1, -1] : ans;

};


lc69 x的平方根(简单)

var mySqrt = function(x) {
    var beg=0
    var end=x
    if(x===1 || x===0)
        return x
    while(end-beg!==1){
        var mid=Math.floor((beg+end)/2)
        if(mid*mid===x)
            return mid
        else if(mid*mid > x){
            end=mid
        }
        else{
            beg=mid
        }
    }
    return beg
};

lc875 爱吃香蕉的珂珂(中等)

这道题可能刚开始转换不了二分查找。仔细分析;

var minEatingSpeed = function(piles, h) {
    //最快速度是,吃的是最大堆每个小时
    //最慢速度一根,在这里面进行查找找到,能在h中吃完的最小速度

    let fast = Math.max(...piles);
    let slow = 1;
    let speed = fast;

    while(slow < fast){
        let time = 0;
        let mid = Math.floor((fast + slow)/2);
        for(let item of piles){
            time += Math.ceil(item/mid);
        }

        if(time > h){
            slow = mid + 1;
        }else{
            fast = mid;
            speed = mid;

        }
    }

    return  speed;

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值