饕餮盛宴——滑动窗口(尺取法)

饕餮盛宴——滑动窗口(尺取法)

注意:此章节作为复习使用,直接代码实现,不写思路。

LeetCode1004

代码实现

class Solution {
    public int longestOnes(int[] A, int K) {
        int left = 0;
        int right = 0;
        int len = 0;
        // 0计数器
        int K1= 0;
        int nums = 0;
        while (right < A.length) {
            if (A[right] == 1) {
                nums++;
            }
            else {
                K1++;
            } 
            right++;
            while (K1 > K) {
                if (A[left] == 1) {
                    nums--;
                }
                else {
                    K1--;
                }
                left++;
            }
            len = Math.max(right - left, len);
        }
        return len;
    }
}

LeetCode239

代码实现

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int left = 0;
        int[] window = new int[nums.length - k + 1];
        int j = 0;
        int right = k;
        while (right <= nums.length) {
            int maxNum = nums[left];
            for (int i = left; i < right; i++) {
                maxNum = Math.max(maxNum, nums[i]);
            }
            window[j] = maxNum;
            right++;
            left++;
            j++;
        }
        return window;
    }
}
// 如果需要在自己的IDE上输出,则只需要调用Arrays.toString()方法即可。
// PS:目前这种方法似乎力扣提交显示超时,后面方法多了再说,毕竟是自己手工做的o( ̄▽ ̄)d 

LeetCode424

代码实现

class Solution {
    public int characterReplacement(String s, int k) {
        // 建立哈希表存储每个字母出现的次数
        HashMap<Character, Integer> record = new HashMap<>();
        // 初始化滑动窗口
        int left = 0;
        int right = 0;
        int len = 0;
        int num = 0;
        while (right < s.length()) {
            char c = s.charAt(right);
            right++;
            // 初始化哈希表
            record.put(c, record.getOrDefault(c, 0) + 1);
            // 找到哈希表中出现次数最多的字母的值
            num = Math.max(num, record.get(c));
            len = right - left;
            // 收缩窗口的时机:当出现次数加上可以替换的次数大于窗口长度时需要收缩窗口
            while (num + k < len) {
                char c1 = s.charAt(left);
                left++;
                len = right - left;
                // 更新数据
                record.put(c1, record.get(c1) - 1);
            }
        }
        if (num + k > s.length()) {
            return s.length();
        }
        return num + k;
    }
}

LeetCode480

代码实现

class Solution {
    public double[] medianSlidingWindow(int[] nums, int k) {
        int left = 0;
        int right = k;
        int i = 0;
        double[] result = new double[nums.length - k + 1];
        int[] median = new int[k];
        while (right <= nums.length) {
            double medianNum = 0.0;
            System.arraycopy(nums, left, median, 0, k);
            Arrays.sort(median);
            if (k % 2 == 0) {
                // 这里强制类型转换是为了防止数组元素超过int型范围
                medianNum = (double)((long)median[k / 2] + (long)median[k / 2 - 1]) / 2;
            }
            else {
                medianNum = median[k / 2];
            }
            result[i] = medianNum;
            right++;
            left++;
            i++;
        }
        return result;
    }
}

这道题暴力加滑动窗口思想解决,效率低下,暂时就这样。

LeetCode992

代码实现

class Solution {
    public int subarraysWithKDistinct(int[] A, int K) {
        return atMostKDistinct(A, K) - atMostKDistinct(A, K - 1);
    }

    /**
     * @param A
     * @param K
     * @return 最多包含 K 个不同整数的子区间的个数
     */
    private int atMostKDistinct(int[] A, int K) {
        int left = 0;
        int right = 0;
        int result = 0;
        HashMap<Integer, Integer> window = new HashMap<>();
        while (right < A.length) {
            int a = A[right];
            window.put(a, window.getOrDefault(a, 0) + 1);
            right++;
            while (window.size() > K) {
                int b = A[left];
                left++;
                window.put(b, window.get(b) - 1);
                if (window.get(b) == 0) {
                    window.remove(b);
                }
            }
            result += right - left;
        }
        return result;
    }
    
}

问:为什么可以用right - left 来表示每次新增子数组?

答:举例说明,[A,B,C]变为[A,B,C,D]新增子数组有[D],[C,D],[B,C,D],[A,B,C,D]。即新数组长度。

注意:遇见恰好转换为最值问题比较适合用滑动窗口。

LeetCode713


与992类似。不过更加简单,理解思路很重要。直接写代码~

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int left = 0;
        int right = 0;
        int mul = 1;
        int result = 0;
        while (right < nums.length) {
            int numR = nums[right];
            mul *= numR;
            right++;
            while (mul >= k) {
                // 注意对特殊情况的处理
                if (k <= 1) {
                    return 0;
                }
                int numL = nums[left];
                left++;
                mul /= numL;
            }
            // 关键点
            result += right - left;
        }
        return result;
    }
}

LeetCode904

代码实现

class Solution {
    public int totalFruit(int[] tree) {
        int left = 0;
        int right = 0;
        int result = 0;
        HashMap<Integer,Integer> window = new HashMap<>();
        while (right < tree.length) {
            int a = tree[right];
            right++;
            window.put(a, window.getOrDefault(a, 0) + 1);
            while (window.size() > 2) {
                int b = tree[left];
                left++;
                window.put(b, window.get(b) - 1);
                if (window.get(b) == 0) {
                    window.remove(b);
                }
            }
            // 每次保留窗口长度最大值
            result = Math.max(result, right - left);
        }
        return result;
    }
}

LeetCode1358

代码实现

class Solution {
    public int numberOfSubstrings(String s) {
        if (s.length() < 3) {
            return 0;
        }
        if (s == null) {
            return 0;
        }
        int left = 0;
        int right = 0;
        int res = 0;
        HashMap<Character, Integer> window = new HashMap<>();
        while (right < s.length()) {
            char ch = s.charAt(right);
            right++;
            window.put(ch, window.getOrDefault(ch, 0) + 1);
            while (window.getOrDefault('a', 0) > 0 && window.getOrDefault('b', 0) > 0 && window.getOrDefault('c', 0) > 0)            {
                char ch1 = s.charAt(left);
                res += s.length() - right + 1;
                left++;
                window.put(ch1, window.get(ch1) - 1);

            }
        }
        return res;
    }
}

当然,由于数据量不大,也可以考虑用数组记录结果就行。代码如下:

class Solution {
    public int numberOfSubstrings(String s) {
        if (s.length() < 3) {
            return 0;
        }
        if (s == null) {
            return 0;
        }
        int left = 0;
        int right = 0;
        int res = 0;
        int[] ans = new int[s.length()];
        while (right < s.length()) {
            char ch = s.charAt(right);
            right++;
            ans[ch - 'a']++;
            while (ans[0] > 0 && ans[1] > 0 && ans[2] > 0) {
                char ch1 = s.charAt(left);
                res += s.length() - right + 1;
                left++;
                ans[ch1 - 'a']--;
            }
        }
        return res;
    }
}

未完待续,还有不少类似的题目,先到这里,融会贯通,掌握思想最重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值