0
点赞
收藏
分享

微信扫一扫

长度最小的子数组[经典可变滑动窗口 || 前缀和 + 二分]

经典可变滑动窗口 || 前缀和 + 二分

前言

保存对连续子数组对滑动窗口的敏感性;一题多解,才能深入理解问题,进而举一反三;二分细节多,值得练习。

趣事

趣事:看到评论区说O(n) > O(nlogn),当logn小于1时。

我第一感觉不可能,毕竟O(nlogn)是扫描n趟,每趟O(logn),肯定比O(n)花费时间多。

仔细一想,当n都很小时,其实就不存在什么O(n),O(logn)了,遵照时间复杂度的定义,都应该属于O(1)

一、长度最小的子数组

在这里插入图片描述

二、多角度解题

package com.xhu.offer.everyday;

//长度最小的子数组
public class MinSubArrayLen {
    /*
    target:找到长度最小的连续子数组,要求和大于等于`target`.
    如何找到连续子数组的和大于target?不断扩大窗口,且用一个变量记录窗口的和,和大于target时就满足条件,除此之外,缩小窗口左端部分,寻找满足条件的子数组。
    如何求最小?用一个变量记录每次满足条件的子数组长度的最小长度。
    M1:滑动可变窗口 + min变量记录最短长度
     */
    public int minSubArrayLen(int target, int[] nums) {
        int min = 1 << 30, n = nums.length, sum = 0;
        int left = 0;
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            if (sum >= target) {
                while ((sum -= nums[left++]) >= target) ;

                min = Math.min(min, i + 2 - left);
            }
        }
        //bug1:没有判断是否不存在sum >= target,此时应该返回0
        return min != 1 << 30 ? min : 0;
    }
}

/*
趣事:看到评论区说O(n) > O(nlogn),当logn小于1时。
我第一感觉不可能,毕竟O(nlogn)是扫描n趟,每趟O(logn),肯定比O(n)花费时间多。
仔细一想,当n都很小时,其实就不存在什么O(n),O(logn)了,遵照时间复杂度的定义,都应该属于O(1)
 */
//思维扩展
class MinSubArrayLen2 {
    /*
    M2-前缀和 + 二分
     */
    public int minSubArrayLen(int target, int[] nums) {
        int min = 1 << 30, n = nums.length, sum = 0;

        int[] prefix = new int[n];
        //前缀和赋值
        for (int i = 0; i < n; i++) prefix[i] = sum += nums[i];
        //开始二分找满足条件的前缀和
        for (int i = 0; i < n; i++) {
            int gap = prefix[i] - target;
            if (gap >= 0) {
                int idx = binarySearch(prefix, gap);
                min = Math.min(min,i - idx + (prefix[idx] == gap ? 0 : 1));
            }
        }
        //没有判断是否不存在sum == target,此时应该返回0
        return min != 1 << 30 ? min : 0;
    }

    /**
     * 插入式二分,插左。
     *
     * @param nums
     * @param target
     * @return
     */
    private int binarySearch(int[] nums, int target) {
        int low = 0, high = nums.length;

        while (low < high) {
            int mid = low + (high - low >>> 1);
            int midVal = nums[mid];

            if (midVal < target) low = mid + 1;
            else high = mid;
        }
        return high;
    }
}

总结

1)保存对连续子数组对滑动窗口的敏感性,才能把各种知识串起来,再进行知识组合,就能解决难题。目的:遇到难题,可根据关键词拆解成相关子问题,找到紧密关联知识点,进行有机组合并解题。
2)一题多解,多角度刨析问题,榨干题的考察价值,做到深入理解,融会贯通。目的:遇到新问题,实现举一反三。
3)二分细节多,紧密关联的关键词–有序数组、集合等。不同的细节由不同的二分需求而来,如二分找值,二分插入,二分重复数据取左取右。

参考文献

[1] LeetCode 长度最小子数组

举报

相关推荐

0 条评论