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

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;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

React-Redux 对Todolist修改

在单独使用redux的时候 需要手动订阅store里面 感觉特别麻烦 不错的是react有一个组件可以帮我们解决这个问题, 那就是react-redux。 react-redux提供了Provider 和 connent给我们使用。 先说一下几个重点知道的知识 Provider 就是用来提供store里面的状态 自动getState()co…

MySQL——视图(VIEW)详解

今天我们一起来学起视图(VIEW)&#xff0c;那么视图是什么呢&#xff1f;视图有什么作用呢&#xff1f;视图一方面可以帮我们使用表的一部分而不是所有的表&#xff0c;另一方面也可以针对不同的用户制定不同的查询视图&#xff01;带着问题一起来寻找答案吧~~~ 1. 常见的数据库…

Zookeeper概述

​ ZooKeeper概述 ZooKeeper是什么 zookeeper是一个为分布式应用程序提供的一个分布式开源协调服务框架。是Google的Chubby的一个开源实现&#xff0c;是Hadoop和Hbase的重要组件。主要用于解决分布式集群中应用系统的一致性问题。提供了基于类似Unix系统的目录节点树方式的数…

python3 爬虫相关学习9:BeautifulSoup 官方文档学习

目录 1 BeautifulSoup 官方文档 2 用bs 和 requests 打开 本地html的区别&#xff1a;代码里的一段html内容 2.1 代码和运行结果 2.2 用beautiful 打开 本地 html 文件 2.2.1 本地html文件 2.2.2 soup1BeautifulSoup(html1,"lxml") 2.3 用requests打开 本地 h…

【默认端口】市面上各种中间件、软件、服务的默认端口汇总

常用软件&#xff0c;中间件&#xff0c;服务的默认端口汇总 常用软件默认端口汇总 市面上各种中间件、软件和服务的默认端口众多&#xff0c;下面列举一些常见的默认端口&#xff1a; SSH&#xff08;Secure Shell&#xff09;&#xff1a;22 Telnet&#xff1a;23 FTP…

赛宁网安助力智能网联汽车发展 | “饶派杯”XCTF车联网安全挑战赛圆满收官

​​ 2023年5月31日&#xff0c;“饶派杯”XCTF车联网安全挑战赛在江西省上饶市圆满落幕。本次大赛特邀国内21支精英战队参与比拼&#xff0c;参赛选手覆盖全国知名高校、自动驾驶汽车和科研院所等车联网安全人才。最终&#xff0c;经过9个小时激烈角逐&#xff0c;来自南京邮电…

后端(二):Servlet

我们上一张聊的是Tomcat&#xff0c;它其实就是一个 HTTP 服务器&#xff0c;而Servlet 是基于 Tomcat 的 原生api &#xff0c;除了 Servlet&#xff0c;后面还有聊到很多 api 。 Servlet 是什么 Servlet&#xff08;Server Applet&#xff09;是Java Servlet的简称&#xf…

动态规划算法(多状态dp1)

动态规划算法专辑之多状态dp问题&#xff08;1&#xff09; 一、什么是多状态 多状态dp问题&#xff0c;指一个规模问题下存在多种状态&#xff0c;我们需要联合关注多种状态间的相互转移&#xff0c;才可以求解目的问题。 多状态问题可以理解为有限状态机&#xff0c;在有限…

centos 7 安装git并配置ssh

一、安装 1、查看是否安装git <span style"color:#333333"><span style"background-color:#ffffff"><code class"language-perl">rpm -qa|<span style"color:#0000ff">grep</span> git </code>…

边缘检测笔记

边缘是什么&#xff1f; 图像的边缘是指图像局部区域中亮度变化明显的部分&#xff0c;边缘位于像素的灰度值产生突变的地方。 边缘的正负之分&#xff1a;由暗到亮为正&#xff0c;由亮变暗为负。 图像的高频信号和低频信号 简单理解为&#xff0c;图像中高频分量&#xff08…

mcu:利用Cortex-M中的DWT实现高精度计时

1、Cortex-M中的DWT 在Cortex-M里面有一个外设叫DWT(Data Watchpoint and Trace)&#xff0c;是用于系统调试及跟踪。 它有一个32位的寄存器叫CYCCNT&#xff0c;它是一个向上的计数器&#xff0c;记录的是内核时钟运行的个数&#xff0c;内核时钟跳动一次&#xff0c;该计数器…

YOLOV5 训练

YOLOV5训练过程 CUDA 和cuDnnan 安装教程 windows上安装可以参考这篇知乎文章 数据集准备 自己准备数据集 可以使用 labelImg 工具&#xff0c;直接 pip install labelimg 就可以安装了。 命令行中输入 labelImg 就可以运行 标注数据的输出结果有多种过格式&#xff0c;V…

前端什么最难学?

前言 个人认为是JS&#xff0c;无论是在平时的项目或者找工作时候JS都是大头&#xff0c;相比起其他的部分&#xff0c;它相对而言是难一点&#xff0c;同时也是十分重要的一部分&#xff0c;学好原生JS&#xff0c;后续的学习才能基于此循序渐进&#xff0c;下面是我总结的关…

GIT学习笔记

团队使用GIT有些时间了&#xff0c;也遇到一些问题&#xff1a; 遇到大量冲突&#xff0c;解决完之后&#xff0c;没有修改的代码也变成蓝色了&#xff0c;如果不push&#xff0c;代码将会丢失代码丢失&#xff08;具体情况&#xff0c;我暂时记不清了&#xff09;git push失败…

数据库入门下篇(如何安装和登录MYSQL数据库)

在这篇文章里&#xff0c;笔者将着重讲解如何在win和Linux系统上安装自己的MySQL数据库软件&#xff0c;以及安装好数据库软件后如何启动和登录&#xff0c;忘了密码怎么办&#xff1f;如何创建一个数据库&#xff0c;如何在数据库中创建一个表等内容 目录 在windows系统上安装…

HNU计算机体系结构-实验一:RISC-V指令理解

HNU计算机体系结构-实验一 前言1.实验目的2.实验步骤1.安装模拟器Ripes2.生成汇编指令3.思考问题1&#xff09;指令add x15, x14, x152&#xff09;指令bge x15 x14 -683&#xff09;指令lw x15, -20 x84&#xff09;指令sw x15, -20 x85&#xff09;简述BranchE信号的作用6&am…

中级前端笔试面试题总结

typeof null 的结果是什么&#xff0c;为什么&#xff1f; typeof null 的结果是Object。 在 JavaScript 第一个版本中&#xff0c;所有值都存储在 32 位的单元中&#xff0c;每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储值的真实数据。类型标签存储在每个单元的…

(浙大陈越版)数据结构 第三章 树(中) 二叉搜索树和平衡二叉树

目录 4.1.1 二叉搜索树及查找 什么是二叉搜索树 定义 二叉搜索树特殊函数集&#xff1a; 查找操作&#xff1a;Find 算法思想 代码实现 补&#xff1a;查找最大和最小元素 4.1.2 二叉搜索树的插入 插入操作&#xff1a;Insert 算法思想 代码实现 例题 4.1.3 二叉…

吴恩达老师《机器学习》课后习题1之线性回归

在学习这些内容之前&#xff0c;需要学习python数据分析相关内容&#xff1a; numpy&#xff1a;科学计算库&#xff0c;处理多维数组&#xff0c;进行数据分析 pandas&#xff1a;基于numpy的一种工具&#xff0c;该工具是为了解决数据分析任务而创建的 matplotlib&#xff1a…

如何进行微服务测试?

微服务测试是一种特殊的测试类型&#xff0c;因为它涉及到多个独立的服务。以下是进行微服务测试的一般性步骤&#xff1a; 1. 确定系统架构 了解微服务架构对成功测试至关重要。确定每个微服务的职责、接口、依赖项和通信方式。了解这些信息可以帮助您更好地规划测试用例和测…