算法刷题-数组-长度最小的子数组

news2025/1/31 3:14:09

209.长度最小的子数组

力扣题目链接

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

示例:

输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

提示:

  • 1 <= target <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

思路

暴力解法

这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2)。

代码如下:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX; // 最终的结果
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= s) { // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};
  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)

后面力扣更新了数据,暴力解法已经超时了。

滑动窗口

接下来就开始介绍数组操作中另一个重要的方法:滑动窗口

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。

那么滑动窗口如何用一个for循环来完成这个操作呢。

首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。

如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?

此时难免再次陷入 暴力解法的怪圈。

所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。

那么问题来了, 滑动窗口的起始位置如何移动呢?

这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:

在这里插入图片描述

最后找到 4,3 是最短距离。

其实从动画中可以发现滑动窗口也可以理解为双指针法的一种!只不过这种解法更像是一个窗口的移动,所以叫做滑动窗口更适合一些。

在本题中实现滑动窗口,主要确定如下三点:

  • 窗口内是什么?
  • 如何移动窗口的起始位置?
  • 如何移动窗口的结束位置?

窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。

窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

解题的关键在于 窗口的起始位置如何移动,如图所示:

leetcode_209

可以发现滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。

C++代码如下:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0; // 滑动窗口数值之和
        int i = 0; // 滑动窗口起始位置
        int subLength = 0; // 滑动窗口的长度
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

一些录友会疑惑为什么时间复杂度是O(n)

不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。

相关题目推荐

  • 904.水果成篮
  • 76.最小覆盖子串

其他语言版本

Java:

class Solution {

    // 滑动窗口
    public int minSubArrayLen(int s, int[] nums) {
        int left = 0;
        int sum = 0;
        int result = Integer.MAX_VALUE;
        for (int right = 0; right < nums.length; right++) {
            sum += nums[right];
            while (sum >= s) {
                result = Math.min(result, right - left + 1);
                sum -= nums[left++];
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

Python:

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        res = float("inf")   # 定义一个无限大的数
        Sum = 0     # 滑动窗口数值之和
        i = 0      # 滑动窗口起始位置
        for j in range(len(nums)):
            Sum += nums[j]
            while Sum >= s:
                res = min(res, j-i+1)
                Sum -= nums[i]
                i += 1
        return 0 if res == float("inf") else res

Go:

func minSubArrayLen(target int, nums []int) int {
    i := 0
    l := len(nums)  // 数组长度
    sum := 0        // 子数组之和
    result := l + 1 // 初始化返回长度为l+1,目的是为了判断“不存在符合条件的子数组,返回0”的情况
    for j := 0; j < l; j++ {
        sum += nums[j]
        for sum >= target {
            subLength := j - i + 1
            if subLength < result {
                result = subLength
            }
            sum -= nums[i]
            i++
        }
    }
    if result == l+1 {
        return 0
    } else {
        return result
    }
}

JavaScript:

var minSubArrayLen = function(target, nums) {
    let start, end
    start = end = 0
    let sum = 0
    let len = nums.length
    let ans = Infinity
    
    while(end < len){
        sum += nums[end];
        while (sum >= target) {
            ans = Math.min(ans, end - start + 1);
            sum -= nums[start];
            start++;
        }
        end++;
    }
    return ans === Infinity ? 0 : ans
};

Typescript:

function minSubArrayLen(target: number, nums: number[]): number {
    let left: number = 0, right: number = 0;
    let res: number = nums.length + 1;
    let sum: number = 0;
    while (right < nums.length) {
        sum += nums[right];
        if (sum >= target) {
            // 不断移动左指针,直到不能再缩小为止
            while (sum - nums[left] >= target) {
                sum -= nums[left++];
            }
            res = Math.min(res, right - left + 1);
        }
        right++;
    }
    return res === nums.length + 1 ? 0 : res;
};

Swift:

func minSubArrayLen(_ target: Int, _ nums: [Int]) -> Int {
    var result = Int.max
    var sum = 0
    var starIndex = 0
    for endIndex in 0..<nums.count {
        sum += nums[endIndex]

        while sum >= target {
            result = min(result, endIndex - starIndex + 1)
            sum -= nums[starIndex]
            starIndex += 1
        }
    }

    return result == Int.max ? 0 : result
}

Rust:

impl Solution {
    pub fn min_sub_array_len(target: i32, nums: Vec<i32>) -> i32 {
        let (mut result, mut subLength): (i32, i32) = (i32::MAX, 0);
        let (mut sum, mut i) = (0, 0);

        for (pos, val) in nums.iter().enumerate() {
            sum += val;
            while sum >= target {
                subLength = (pos - i + 1) as i32;
                if result > subLength {
                    result = subLength;
                }
                sum -= nums[i];
                i += 1;
            }
        }
        if result == i32::MAX {
            return 0;
        }
        result
    }
}

PHP:

// 双指针 - 滑动窗口
class Solution {
    /**
     * @param Integer $target
     * @param Integer[] $nums
     * @return Integer
     */
    function minSubArrayLen($target, $nums) {
        if (count($nums) < 1) {
            return 0;
        }
        $sum = 0;
        $res = PHP_INT_MAX;
        $left = 0;
        for ($right = 0; $right < count($nums); $right++) {
            $sum += $nums[$right];
            while ($sum >= $target) {
                $res = min($res, $right - $left + 1);
                $sum -= $nums[$left];
                $left++;
            }
        }
        return $res == PHP_INT_MAX ? 0 : $res;
    }
}

Ruby:

def min_sub_array_len(target, nums)
  res = Float::INFINITY # 无穷大
  i, sum = 0, 0
  nums.length.times do |j|
    sum	+= nums[j]
    while sum >= target
      res = [res, j - i + 1].min
      sum -= nums[i]
      i	+= 1
    end
  end
  res == Float::INFINITY ? 0 : res
end

C:
暴力解法:

int minSubArrayLen(int target, int* nums, int numsSize){
    //初始化最小长度为INT_MAX
    int minLength = INT_MAX;
    int sum;

    int left, right;
    for(left = 0; left < numsSize; ++left) {
        //每次遍历都清零sum,计算当前位置后和>=target的子数组的长度
        sum = 0;
        //从left开始,sum中添加元素
        for(right = left; right < numsSize; ++right) {
            sum += nums[right];
            //若加入当前元素后,和大于target,则更新minLength
            if(sum >= target) {
                int subLength = right - left + 1;
                minLength = minLength < subLength ? minLength : subLength;
            }
        }
    }
    //若minLength不为INT_MAX,则返回minLnegth
    return minLength == INT_MAX ? 0 : minLength;
}

滑动窗口:

int minSubArrayLen(int target, int* nums, int numsSize){
    //初始化最小长度为INT_MAX
    int minLength = INT_MAX;
    int sum = 0;

    int left = 0, right = 0;
    //右边界向右扩展
    for(; right < numsSize; ++right) {
        sum += nums[right];
        //当sum的值大于等于target时,保存长度,并且收缩左边界
        while(sum >= target) {
            int subLength = right - left + 1;
            minLength = minLength < subLength ? minLength : subLength;
            sum -= nums[left++];
        }
    }
    //若minLength不为INT_MAX,则返回minLnegth
    return minLength == INT_MAX ? 0 : minLength;
}

Kotlin:

class Solution {
    fun minSubArrayLen(target: Int, nums: IntArray): Int {
        var start = 0
        var end = 0
        var ret = Int.MAX_VALUE
        var count = 0
        while (end < nums.size) {
            count += nums[end]
            while (count >= target) {
                ret = if (ret > (end - start + 1)) end - start + 1 else ret
                count -= nums[start++]
            }
            end++
        }
        return if (ret == Int.MAX_VALUE) 0 else ret
    }
}

滑动窗口

class Solution {
   fun minSubArrayLen(target: Int, nums: IntArray): Int {
    // 左边界 和 右边界
    var left: Int = 0
    var right: Int = 0
    // sum 用来记录和
    var sum: Int = 0
    // result记录一个固定值,便于判断是否存在的这样的数组
    var result: Int = Int.MAX_VALUE
    // subLenth记录长度
    var subLength = Int.MAX_VALUE
   

    while (right < nums.size) {
        // 从数组首元素开始逐次求和
        sum += nums[right++]
        // 判断
        while (sum >= target) {
            var temp = right - left 
            // 每次和上一次比较求出最小数组长度
            subLength = if (subLength > temp) temp else subLength
            // sum减少,左边界右移
            sum -= nums[left++]
        }
    }
    // 如果subLength为初始值,则说明长度为0,否则返回subLength
    return if(subLength == result) 0 else subLength
    }
}

Scala:

滑动窗口:

object Solution {
  def minSubArrayLen(target: Int, nums: Array[Int]): Int = {
    var result = Int.MaxValue // 返回结果,默认最大值
    var left = 0 // 慢指针,当sum>=target,向右移动
    var sum = 0 // 窗口值的总和
    for (right <- 0 until nums.length) {
      sum += nums(right)
      while (sum >= target) {
        result = math.min(result, right - left + 1) // 产生新结果
        sum -= nums(left) // 左指针移动,窗口总和减去左指针的值
        left += 1 // 左指针向右移动
      }
    }
    // 相当于三元运算符,return关键字可以省略
    if (result == Int.MaxValue) 0 else result
  }
}

暴力解法:

object Solution {
  def minSubArrayLen(target: Int, nums: Array[Int]): Int = {
    import scala.util.control.Breaks
    var res = Int.MaxValue
    var subLength = 0
    for (i <- 0 until nums.length) {
      var sum = 0
      Breaks.breakable(
        for (j <- i until nums.length) {
          sum += nums(j)
          if (sum >= target) {
            subLength = j - i + 1
            res = math.min(subLength, res)
            Breaks.break()
          }
        }
      )
    }
    // 相当于三元运算符
    if (res == Int.MaxValue) 0 else res
  }
}

C#:

public class Solution {
    public int MinSubArrayLen(int s, int[] nums) {
        int n = nums.Length;
        int ans = int.MaxValue;
        int start = 0, end = 0;
        int sum = 0;
        while (end < n)  {
            sum += nums[end];
            while (sum >= s) 
            {
                ans = Math.Min(ans, end - start + 1);
                sum -= nums[start];
                start++;
            }
            end++;
        }
        return ans == int.MaxValue ? 0 : ans;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/631768.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子元器件解析之电容(一)——定义与性能参数

下篇文章&#xff1a;电子元器件解析之电容(二)——电容分类与应用场景&#xff1a;https://blog.csdn.net/weixin_42837669/article/details/131142767 摘要 电容是最基本的电子元器件之一&#xff0c;本文介绍了电容的定义&#xff0c;并总结了电容的各个性能参数&#xff0c…

攻防世界—easyupload

2023.6.10 又试了一下 他好像会检测php字段&#xff0c;所以一开始的aaa.jpg怎么也传不上去&#xff0c;用a.jpg那种就可以了 根据题目和页面我们可以猜到这应该是一个利用文件上传漏洞&#xff0c;利用一句话木马来获取服务器上保存的flag保险起见右键查看源代码 可以发现毫无…

基础实验篇 | RflySim底层飞行控制算法开发系列课程总体介绍

本讲主要介绍多旋翼的特点及选用多旋翼作为实验平台的原因、对于无人系统教育的一些新需求、RflySim平台对于飞控的底层控制算法的开发优势、本期平台课程的设置、以及如何开发自驾仪系统。 相较于固定翼和直升机&#xff0c;多旋翼具有机械结构简单、 易维护的优点。以四旋翼…

使用 Pycharm 调试远程代码

文章目录 背景同步远程代码Interpreter注意点 背景 工作机是一台 Windows 电脑&#xff0c;而很多时候需要在 Mac 电脑上编码、配合 iPhone 模拟器&#xff0c;所以我以前是用 VNC 或者向日葵来远程 Mac 来编程&#xff0c;其实还能接受&#xff0c;但是最让我不舒服的是快捷键…

MySQL表操作:提高数据处理效率的秘诀(进阶)(2)

&#x1f495;“学习难免有坎坷&#xff0c;重要的是你能尽力而为&#xff0c;持之以恒。”&#x1f495; &#x1f43c;作者&#xff1a;不能再留遗憾了&#x1f43c; &#x1f386;专栏&#xff1a;MySQL学习&#x1f386; &#x1f697;本文章主要内容&#xff1a;MySQL表操…

K Shortest Paths算法之Yen algorithm

Yen’s算法是一种在图论中用于计算单源K最短无环路径的算法&#xff0c;该算法由Jin Y. Yen在1971年提出。这个算法的时间复杂度和空间复杂度都取决于用于计算偏离路径的最短路径算法。如果使用Dijkstra算法&#xff0c;那么时间复杂度为O(KN3)&#xff0c;采用Fibonacci堆计算…

Elasticsearch:Explain API - 如何计算分数

你想了解你的文档为何获得该分数吗&#xff1f; 文档 让我们通过一组示例文档来了解 Explain API。 就我而言&#xff0c;我将使用一小部分电影名言。 POST _bulk { "index" : { "_index" : "movie_quotes" } } { "title" : "T…

从malloc到跑路

当我还是一个懵懂无知的少年时&#xff0c;内心也曾升起这样的疑问这内存咋来的捏❓ 有个帅气的小哥哥给我甩了一篇博客&#xff1a;对&#xff0c;就是这篇&#xff0c;看完后总感觉意犹未尽&#xff0c;似乎少了点什么的样子。。。 还是从malloc开始 如果申请的内存小于64B…

“配置DHCP服务器和DHCP中继的网络自动配置实验“

"配置DHCP服务器和DHCP中继的网络自动配置实验" 【实验目的】 部署DHCP服务器。熟悉DHCP中继的配置方法。验证拓扑。 【实验拓扑】 实验拓扑如图所示。 设备参数如下表所示。 设备 接口 IP地址 子网掩码 默认网关 DHCPSERVE S0/3/0 192.168.10.1 255.255.…

丰富上下文的超高分辨率分割:一种新的基准

文章目录 Ultra-High Resolution Segmentation with Ultra-Rich Context: A Novel Benchmark摘要数据集Dataset SummaryData Collection and Pre-processing 数据标注数据统计 WSDNet实验结果 Ultra-High Resolution Segmentation with Ultra-Rich Context: A Novel Benchmark …

SSM整合快速入门案例(一)

文章目录 前言一、设计数据库表二、创建工程三、SSM技术整合四、功能模块开发五、接口测试总结 前言 为了巩固所学的知识&#xff0c;作者尝试着开始发布一些学习笔记类的博客&#xff0c;方便日后回顾。当然&#xff0c;如果能帮到一些萌新进行新技术的学习那也是极好的。作者…

不认识docker,怎么好意思说自己是干IT的

1.Docker是什么 一款产品从开发到上线&#xff0c;从操作系统&#xff0c;到运行环境&#xff0c;再到应用配置。作为开发运维之间的协作我们需要关心很多东西&#xff0c;这也是很多互联网公司都不得不面对的问题&#xff0c;特别是各种版本的迭代之后&#xff0c;不同版本环…

用户行为数据分析

文章目录 用户行为数据分析1 项目描述2 项目需求3 数据准备1、创建user_data数据表用于导入user_data.csv中的数据2、加载user_data.csv中的数据到user_data表3、接下来进行数据清洗&#xff0c;包括&#xff1a;删除重复值&#xff0c;时间戳格式化&#xff0c;删除异常值。 4…

OpenGL光照之基础光照

文章目录 环境光照漫反射光照计算漫反射光照镜面光照代码 现实世界的光照是极其复杂的&#xff0c;而且会受到诸多因素的影响&#xff0c;这是我们有限的计算能力所无法模拟的。因此OpenGL的光照使用的是简化的模型&#xff0c;对现实的情况进行近似&#xff0c;这样处理起来会…

MyBatis-Plus(2.0)

ActiveRecord ActiveRecord(简称AR)一直广受动态语言&#xff08;PHP、Ruby等&#xff09;的喜爱&#xff0c;而java作为准静态语言&#xff0c;对于ActiveRecord往往只能感叹器优雅 什么是ActiveRecord&#xff1f; ActiveRecord也属于ORM(对象关系映射)层&#xff0c;由Rail…

视频|人人能看懂的苹果visionOS空间设计课程

本周的重磅消息无疑是苹果Vision Pro以及对应的visionOS&#xff0c;考虑到苹果头显硬件上当前以第一方App为主&#xff0c;因此本届WWDC的一个重点就是释放visionOS和相关能力给开发者&#xff0c;让开发者尽快打造出更多、更优质的第三方App阵容。 与此同时&#xff0c;苹果也…

【vue3】10-vue组件化额外知识补充(下)-动态组件-组件缓存-v-model在组件上的应用

组件化-额外知识补充&#xff08;下&#xff09; 动态组件的使用(了解)keep-alive&#xff08;理解&#xff09;认识keep-alivekeep-alive的使用 异步组件的使用webpack分包处理&#xff08;了解&#xff09;Vue中实现异步组件 组件的v-model组件的混入Mixin&#xff08;了解&a…

阿里云弹性公网EIP收费价格表(按量/包年包月/配置费)

阿里云弹性公网EIP怎么收费&#xff1f;EIP地域不同价格不同&#xff0c;EIP计费模式分为包年包月和按量付费&#xff0c;弹性公网IP可以按带宽收费也可以按使用流量收费&#xff0c;阿里云百科分享阿里云弹性公网IP不同地域、不同计费模式、按带宽和按使用流量详细收费价格表&…

基于springboot+vue技术的在线考试系统源码数据库

源码看这里 https://download.csdn.net/download/2301_76965813/87881785 前台 学生登录&#xff1a; 如果没有账号&#xff0c;首先需要以学生的身份进行注册&#xff0c;即输入姓名、性别、密码以及班级&#xff0c;注册成功之后输入账户名和密码进行登录&#xff0c;进入…

conda虚拟环境配置和系统相关配置

一、conda虚拟环境 首先&#xff0c;尽量别在base中直接安装自己的包。原因有以下两个&#xff1a; 1.base环境是conda运行的基础&#xff0c;没法一键清除&#xff0c;如果损坏base环境要么滚回要么卸载重装&#xff0c;比较麻烦 2.base中很多包并不是项目需要的包&#xff0c…