【滑动窗口】【二分查找】C++算法:和至少为 K 的最短子数组

简介: 【滑动窗口】【二分查找】C++算法:和至少为 K 的最短子数组

LeetCode862:和至少为 K 的最短子数组

给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1 。子数组 是数组中 连续 的一部分。

示例 1:

输入:nums = [1], k = 1

输出:1

示例 2:

输入:nums = [1,2], k = 4

输出:-1

示例 3:

输入:nums = [2,-1,2], k = 3

输出:3

提示:

1 <= nums.length <= 105

-105 <= nums[i] <= 105

1 <= k <= 109

滑动窗口

时间复杂度O(nlogn)。枚举子数组的结尾时间复杂度O(n),计算最佳开始时间复杂度O(logn)。

vPreSum是前缀和。

nums[l,r]的和为vPreSum[r+1]-vPreSum[l] >= k ==>> vPreSum[r+1] - k >= vPreSum[l]

l取值范围:[0,r]。

最短子数组,也就是l最大。也就是满足 vPreSum[l] <= vPreSum[r+1] - k的最大l。

如果l1 < l2 ,且vPreSum[l1] >= vPreSum[l2] ,则l1被淘汰,l2 被淘汰后 vPreSum成升序。我寻找最后一个小于等于vPreSum[r+1] - k的索引。 用std::upper_bound 。

代码

核心代码

//默认升序

template<class T = long long,bool bAsc= true >
class COrderValueIndexVector
{
public:
  COrderValueIndexVector(const vector<T>& vValue):m_vValue(vValue)
  {
  }
  void AddIndex(int index)
  {
    if (bAsc)
    {
      Add<std::less_equal<T>>(index);
    }
    else
    {
      assert(false);
    }
  } 
  //升序:最后一个小于等于的索引 
  int PreUpperBoundIndex(T value)
  {
    const int inx = std::upper_bound(m_vOrderValue.begin(), m_vOrderValue.end(), value) - m_vOrderValue.begin();
    if (inx > 0)
    {
      return m_vInx[inx - 1];
    }
    return -1;
  }
protected:  
  template<class _PR>
  void Add(int index)
  {
    //nums[l,r]的和为vPreSum[r+1]-vPreSum[l] >= k =>vPreSum[r+1] - k >= vPreSum[l]
    while (m_vOrderValue.size() && _PR()(m_vValue[index], m_vOrderValue.back()))
    {
      m_vInx.pop_back();
      m_vOrderValue.pop_back();
    }
    m_vInx.emplace_back(index);
    m_vOrderValue.emplace_back(m_vValue[index]);
  }
  vector<int> m_vInx;
  vector<T> m_vOrderValue;
  const vector<T>& m_vValue;
};
class Solution {
public:
  int shortestSubarray(vector<int>& nums, int k) {
    vector<long long> vPreSum = { 0 };
    for (const auto& n : nums)
    {
      vPreSum.emplace_back(n + vPreSum.back());
    }
    COrderValueIndexVector ov(vPreSum);
    int iRet = INT_MAX;
    for (int r = 0; r < nums.size(); r++)
    {
      //nums[l,r]的和为vPreSum[r+1]-vPreSum[l] >= k =>vPreSum[r+1] - k >= vPreSum[l]
      ov.AddIndex(r);
      const int left = ov.PreUpperBoundIndex(vPreSum[r + 1] - k);
      if (left >= 0 )
      {
        iRet = min(iRet, r - left + 1);
      }     
    }
    return (INT_MAX == iRet) ? -1 : iRet;
  }
};

测试用例

template<class T>
void Assert(const T& t1, const T& t2)
{
  assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
  if (v1.size() != v2.size())
  {
    assert(false);
    return;
  }
  for (int i = 0; i < v1.size(); i++)
  {
    Assert(v1[i], v2[i]);
  }
}
int main()
{
  vector<int> nums;
  int k;
  {
    Solution sln;
    nums = { 1 }, k = 1;
    auto res = sln.shortestSubarray(nums, k);
    Assert(1, res);
  }
  {
    Solution sln;
    nums = { 1,2 }, k = 4;
    auto res = sln.shortestSubarray(nums, k);
    Assert(-1, res);
  }
  {
    Solution sln;
    nums = { 2,-1,2 }, k = 3;
    auto res = sln.shortestSubarray(nums, k);
    Assert(3, res);
  }
//CConsole::Out(res);
}


扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法C++ 实现。

相关文章
|
3月前
|
存储 算法
算法入门:专题二---滑动窗口(长度最小的子数组)类型题目攻克!
给定一个正整数数组和目标值target,找出总和大于等于target的最短连续子数组长度。利用滑动窗口(双指针)优化,维护窗口内元素和,通过单调性避免重复枚举,时间复杂度O(n)。当窗口和满足条件时收缩左边界,更新最小长度,最终返回结果。
|
4月前
|
存储 算法 搜索推荐
软考算法破壁战:从二分查找到堆排序,九大排序核心速通指南
专攻软考高频算法,深度解析二分查找、堆排序、快速排序核心技巧,对比九大排序算法,配套动画与真题,7天掌握45%分值模块。
247 1
软考算法破壁战:从二分查找到堆排序,九大排序核心速通指南
|
存储 算法 C++
【C++数据结构——查找】二分查找(头歌实践教学平台习题)【合集】
二分查找的基本思想是:每次比较中间元素与目标元素的大小,如果中间元素等于目标元素,则查找成功;顺序表是线性表的一种存储方式,它用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的元素在物理存储位置上也相邻。第1次比较:查找范围R[0...10],比较元素R[5]:25。第1次比较:查找范围R[0...10],比较元素R[5]:25。第2次比较:查找范围R[0..4],比较元素R[2]:10。第3次比较:查找范围R[3...4],比较元素R[3]:15。,其中是顺序表中元素的个数。
537 68
【C++数据结构——查找】二分查找(头歌实践教学平台习题)【合集】
|
11月前
|
算法 Java 索引
算法系列之搜素算法-二分查找
二分查找是一种在`有序`数组中查找特定元素的算法。它的基本思想是通过将数组分成两半,逐步缩小查找范围,直到找到目标元素或确定目标元素不存在。
197 9
算法系列之搜素算法-二分查找
|
10月前
|
存储 监控 算法
关于员工上网监控系统中 PHP 关联数组算法的学术解析
在当代企业管理中,员工上网监控系统是维护信息安全和提升工作效率的关键工具。PHP 中的关联数组凭借其灵活的键值对存储方式,在记录员工网络活动、管理访问规则及分析上网行为等方面发挥重要作用。通过关联数组,系统能高效记录每位员工的上网历史,设定网站访问权限,并统计不同类型的网站访问频率,帮助企业洞察员工上网模式,发现潜在问题并采取相应管理措施,从而保障信息安全和提高工作效率。
190 7
|
11月前
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
737 23
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
280 5
|
算法 索引
【算法】——二分查找合集
二分查找基础模版和进阶模版,查找元素位置,搜索插入位置,x的平方根,山脉数组的峰顶索引,寻找峰值,点名
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
271 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
|
算法 C# 索引
C#二分查找算法
C#二分查找算法
171 1